VoiceInput/Sources/App/MenuBarController.swift
JOJO 1d9c9d5fa0 feat: macOS 菜单栏语音输入应用初始版本
- CGEvent cghidEventTap 全局监听 Fn 键
- Apple Speech 流式语音识别(zh-CN 默认)
- 实时音频 RMS 电平驱动波形动画
- NSPanel 胶囊浮动窗口
- 剪贴板 + Cmd+V 文本注入,CJK 输入法自动切换
- OpenAI 兼容 LLM 纠错(DeepSeek 预设)
- LSUIElement 菜单栏运行
- SPM + Makefile 构建
2026-04-24 16:17:57 +08:00

143 lines
4.2 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 let llmSubmenu = NSMenu()
private let llmEnabledItem: NSMenuItem
private let settingsWindow = SettingsWindow()
override init() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
// LLM /
llmEnabledItem = NSMenuItem(
title: "Enable LLM Refinement",
action: #selector(toggleLLM),
keyEquivalent: ""
)
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.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())
// LLM
llmSubmenu.addItem(llmEnabledItem)
llmEnabledItem.target = self
llmEnabledItem.action = #selector(toggleLLM)
updateLLMMenuState()
let settingsItem = NSMenuItem(
title: "LLM Settings...",
action: #selector(openSettings),
keyEquivalent: ","
)
settingsItem.target = self
llmSubmenu.addItem(settingsItem)
let llmSubmenuItem = NSMenuItem()
llmSubmenuItem.title = "LLM Refinement"
llmSubmenuItem.submenu = llmSubmenu
menu.addItem(llmSubmenuItem)
menu.addItem(.separator())
// 退
let quitItem = NSMenuItem(
title: "Quit",
action: #selector(quit),
keyEquivalent: "q"
)
quitItem.target = self
menu.addItem(quitItem)
statusItem.menu = menu
}
private func updateLLMMenuState() {
llmEnabledItem.state = Config.shared.llmEnabled ? .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 toggleLLM() {
Config.shared.llmEnabled.toggle()
updateLLMMenuState()
print("[Menu] LLM Refinement: \(Config.shared.llmEnabled ? "ON" : "OFF")")
}
@objc private func openSettings() {
settingsWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}
@objc private func quit() {
NSApp.terminate(nil)
}
}