From 1efaa6909a70117f74fdd9ecf2c7df8a057fe951 Mon Sep 17 00:00:00 2001 From: impressionyang Date: Thu, 11 Jun 2026 16:13:59 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20CapsLock=20=E7=81=AF=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E5=AE=8C=E6=88=90=E5=90=8E=E5=A4=8D=E4=BD=8D?= =?UTF-8?q?=EF=BC=8C=E7=8A=B6=E6=80=81=E5=AE=8C=E5=85=A8=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=89=98=E7=9B=98=E5=9B=BE=E6=A0=87=E6=8C=87=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CapsLock 灯流程变更: 旧: 按下 → 灯亮 → 1s 灯灭 → 录音 → 松开 → 识别 → 注入 新: 按下 → 灯亮 → 1s 开始录音(灯保持亮) → 松开 → 识别 → 注入 → 灯灭 - 移除 timer callback 中的 simulateCapsLock() - 移除 onHotkeyDeactivated Recording 分支的 simulateCapsLock() - onRecognitionComplete() 开始时调用 simulateCapsLock() 复位灯 - stopRecordingAndTranscribe() 空音频时也复位灯 托盘图标指示: 绿色 ○ — 就绪/静默 黄色 ○ — 等待长按确认 红色 ● — 正在录音(CapsLock 灯亮) 橙色 ◉ — 正在识别 Co-Authored-By: Claude Opus 4.7 --- src/core/voice_input_service.cpp | 17 +++++++++-------- src/core/voice_input_service.h | 12 +++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/core/voice_input_service.cpp b/src/core/voice_input_service.cpp index f3fd1e0..b35eb18 100644 --- a/src/core/voice_input_service.cpp +++ b/src/core/voice_input_service.cpp @@ -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& 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; diff --git a/src/core/voice_input_service.h b/src/core/voice_input_service.h index 7f814aa..cb0625a 100644 --- a/src/core/voice_input_service.h +++ b/src/core/voice_input_service.h @@ -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 {