fix: CapsLock 灯改为识别完成后复位,状态完全通过托盘图标指示

CapsLock 灯流程变更:
  旧: 按下 → 灯亮 → 1s 灯灭 → 录音 → 松开 → 识别 → 注入
  新: 按下 → 灯亮 → 1s 开始录音(灯保持亮) → 松开 → 识别 → 注入 → 灯灭

- 移除 timer callback 中的 simulateCapsLock()
- 移除 onHotkeyDeactivated Recording 分支的 simulateCapsLock()
- onRecognitionComplete() 开始时调用 simulateCapsLock() 复位灯
- stopRecordingAndTranscribe() 空音频时也复位灯

托盘图标指示:
  绿色 ○ — 就绪/静默
  黄色 ○ — 等待长按确认
  红色 ● — 正在录音(CapsLock 灯亮)
  橙色 ◉ — 正在识别

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
impressionyang 2026-06-11 16:13:59 +08:00
parent 0b9ea40d20
commit 1efaa6909a
2 changed files with 18 additions and 11 deletions

View File

@ -40,18 +40,15 @@ VoiceInputService::VoiceInputService(ConfigManager* configManager,
{
impl_->sttEngine = sttEngine;
// 1s 定时器:灯灭 → 开始正式录音
// 1s 定时器:确认长按 → 开始正式录音CapsLock 灯保持 ON识别后复位
longPressTimer_ = new QTimer(this);
longPressTimer_->setSingleShot(true);
connect(longPressTimer_, &QTimer::timeout, this, [this]() {
if (state_ == PreRecording) {
// 复位 CapsLock 灯
simulateCapsLock();
// 进入正式录音
state_ = Recording;
audioBuffer_.clear(); // 清除预录音期间的静音
emit statusChanged("正在录音...");
LOG_DEBUG(kTag, "PreRecording → Recording (灯,开始录音)");
LOG_DEBUG(kTag, "PreRecording → Recording (灯保持 ON,开始录音)");
}
});
@ -185,10 +182,9 @@ void VoiceInputService::onHotkeyDeactivated() {
state_ = Idle;
LOG_DEBUG(kTag, "短按,恢复 CapsLock 灯");
} else if (state_ == Recording) {
// 长按后松开 → 先恢复 CapsLock再开始识别
simulateCapsLock();
// 长按后松开 → 灯保持 ON等待识别完成后复位
state_ = Idle;
LOG_DEBUG(kTag, "Recording → Idle (松开转写)");
LOG_DEBUG(kTag, "Recording → Idle (松开转写,灯保持 ON)");
stopRecordingAndTranscribe();
}
@ -207,6 +203,8 @@ void VoiceInputService::onAudioData(const std::vector<float>& samples, int sampl
void VoiceInputService::stopRecordingAndTranscribe() {
if (audioBuffer_.empty()) {
// 无音频 → 复位 CapsLock 灯
simulateCapsLock();
emit statusChanged("未检测到音频输入");
return;
}
@ -235,6 +233,9 @@ void VoiceInputService::stopRecordingAndTranscribe() {
}
void VoiceInputService::onRecognitionComplete(const QString& text) {
// 识别完成后,复位 CapsLock 灯
simulateCapsLock();
if (text.isEmpty()) {
emit statusChanged("识别结果:无语音输入");
return;

View File

@ -18,13 +18,19 @@ class ConfigManager;
* @brief CapsLock
*
* CapsLock
* (PreRecording) 1s (Recording)
* CapsLock
* (PreRecording) 1s (Recording)
* CapsLock
*
*
* 绿
*
*
*
*
*
* Idle
* PreRecording
* Recording 1s Portal
* Recording 1s Portal
* Cooldown
*/
class VoiceInputService : public QObject {