VoiceInput/Sources/App/MenuBarController.swift

242 lines
7.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Cocoa
///
final class MenuBarController: NSObject {
private let statusItem: NSStatusItem
private let menu = NSMenu()
private var languageMenuItems: [NSMenuItem] = []
private var modeMenuItems: [NSMenuItem] = []
// LLM
private let inputLLMSubmenu = NSMenu()
private let inputLLMEnabledItem: NSMenuItem
private let assistLLMSubmenu = NSMenu()
private let assistLLMEnabledItem: NSMenuItem
private let inputSettingsWindow: SettingsWindow
private let assistSettingsWindow: SettingsWindow
override init() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
inputLLMEnabledItem = NSMenuItem(
title: "Enable",
action: #selector(toggleInputLLM),
keyEquivalent: ""
)
assistLLMEnabledItem = NSMenuItem(
title: "Enable",
action: #selector(toggleAssistLLM),
keyEquivalent: ""
)
inputSettingsWindow = SettingsWindow(mode: .input)
assistSettingsWindow = SettingsWindow(mode: .assist)
super.init()
configureStatusItem()
buildMenu()
}
private func configureStatusItem() {
if let button = statusItem.button {
button.image = NSImage(
systemSymbolName: "mic.fill",
accessibilityDescription: "Voice Input"
)
button.toolTip = "Voice Input — 按住 Fn 键录音"
}
}
private func buildMenu() {
//
let currentLanguageItem = NSMenuItem(
title: "Language: \(Config.shared.language.displayName)",
action: nil,
keyEquivalent: ""
)
currentLanguageItem.isEnabled = false
menu.addItem(currentLanguageItem)
menu.addItem(.separator())
//
let languageMenu = NSMenu()
for lang in RecognitionLanguage.allCases {
let item = NSMenuItem(
title: lang.displayName,
action: #selector(selectLanguage(_:)),
keyEquivalent: ""
)
item.target = self
item.representedObject = lang
item.state = lang == Config.shared.language ? .on : .off
languageMenu.addItem(item)
languageMenuItems.append(item)
}
let languageSubmenuItem = NSMenuItem()
languageSubmenuItem.title = "Language"
languageSubmenuItem.submenu = languageMenu
menu.addItem(languageSubmenuItem)
menu.addItem(.separator())
// ===== =====
let modeMenu = NSMenu()
let inputModeItem = NSMenuItem(
title: "🎤 语音输入",
action: #selector(selectMode(_:)),
keyEquivalent: ""
)
inputModeItem.target = self
inputModeItem.representedObject = TriggerMode.input
inputModeItem.state = Config.shared.triggerMode == .input ? .on : .off
modeMenuItems.append(inputModeItem)
modeMenu.addItem(inputModeItem)
let assistModeItem = NSMenuItem(
title: "💬 AI 助手",
action: #selector(selectMode(_:)),
keyEquivalent: ""
)
assistModeItem.target = self
assistModeItem.representedObject = TriggerMode.assist
assistModeItem.state = Config.shared.triggerMode == .assist ? .on : .off
modeMenuItems.append(assistModeItem)
modeMenu.addItem(assistModeItem)
let modeSubmenuItem = NSMenuItem()
modeSubmenuItem.title = "Mode"
modeSubmenuItem.submenu = modeMenu
menu.addItem(modeSubmenuItem)
menu.addItem(.separator())
// ===== Input LLM =====
inputLLMSubmenu.addItem(inputLLMEnabledItem)
inputLLMEnabledItem.target = self
inputLLMEnabledItem.action = #selector(toggleInputLLM)
updateInputLLMMenuState()
let inputSettingsItem = NSMenuItem(
title: "Settings...",
action: #selector(openInputSettings),
keyEquivalent: ""
)
inputSettingsItem.target = self
inputLLMSubmenu.addItem(inputSettingsItem)
let inputSubmenuItem = NSMenuItem()
inputSubmenuItem.title = "🎤 Input LLM"
inputSubmenuItem.submenu = inputLLMSubmenu
menu.addItem(inputSubmenuItem)
menu.addItem(.separator())
// ===== Assist LLM =====
assistLLMSubmenu.addItem(assistLLMEnabledItem)
assistLLMEnabledItem.target = self
assistLLMEnabledItem.action = #selector(toggleAssistLLM)
updateAssistLLMMenuState()
let assistSettingsItem = NSMenuItem(
title: "Settings...",
action: #selector(openAssistSettings),
keyEquivalent: ""
)
assistSettingsItem.target = self
assistLLMSubmenu.addItem(assistSettingsItem)
let assistSubmenuItem = NSMenuItem()
assistSubmenuItem.title = "💬 Assist LLM"
assistSubmenuItem.submenu = assistLLMSubmenu
menu.addItem(assistSubmenuItem)
menu.addItem(.separator())
// 退
let quitItem = NSMenuItem(
title: "Quit",
action: #selector(quit),
keyEquivalent: "q"
)
quitItem.target = self
menu.addItem(quitItem)
statusItem.menu = menu
}
private func updateInputLLMMenuState() {
inputLLMEnabledItem.state = Config.shared.inputLLM.enabled ? .on : .off
}
private func updateAssistLLMMenuState() {
assistLLMEnabledItem.state = Config.shared.assistLLM.enabled ? .on : .off
}
private func updateModeMenuState() {
for item in modeMenuItems {
guard let mode = item.representedObject as? TriggerMode else { continue }
item.state = mode == Config.shared.triggerMode ? .on : .off
}
}
private func updateLanguageMenuState() {
for item in languageMenuItems {
guard let lang = item.representedObject as? RecognitionLanguage else { continue }
item.state = lang == Config.shared.language ? .on : .off
}
if let first = menu.items.first {
first.title = "Language: \(Config.shared.language.displayName)"
}
}
@objc private func selectLanguage(_ sender: NSMenuItem) {
guard let lang = sender.representedObject as? RecognitionLanguage else { return }
Config.shared.language = lang
updateLanguageMenuState()
print("[Menu] Language switched to: \(lang.displayName)")
}
@objc private func selectMode(_ sender: NSMenuItem) {
guard let mode = sender.representedObject as? TriggerMode else { return }
Config.shared.triggerMode = mode
updateModeMenuState()
print("[Menu] Mode switched to: \(mode)")
}
@objc private func toggleInputLLM() {
var config = Config.shared.inputLLM
config.enabled.toggle()
Config.shared.inputLLM = config
updateInputLLMMenuState()
print("[Menu] Input LLM: \(config.enabled ? "ON" : "OFF")")
}
@objc private func toggleAssistLLM() {
var config = Config.shared.assistLLM
config.enabled.toggle()
Config.shared.assistLLM = config
updateAssistLLMMenuState()
print("[Menu] Assist LLM: \(config.enabled ? "ON" : "OFF")")
}
@objc private func openInputSettings() {
inputSettingsWindow.makeKeyAndOrderFront(nil)
NSApplication.shared.activate(ignoringOtherApps: true)
}
@objc private func openAssistSettings() {
assistSettingsWindow.makeKeyAndOrderFront(nil)
NSApplication.shared.activate(ignoringOtherApps: true)
}
@objc private func quit() {
NSApplication.shared.terminate(nil)
}
}