Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions openless-all/app/src-tauri/src/coordinator/dictation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ pub(super) async fn handle_pressed_edge(inner: &Arc<Inner>) {
let was_held = inner.hotkey_trigger_held.swap(true, Ordering::SeqCst);
if !was_held {
// 路由:QA 浮窗可见时,rightOption 边沿走 QA;否则走主听写。详见 issue #118 v2。
// 例外:dictation session 已经在跑(Starting / Listening / Processing / Inserting),
// 即使 QA 浮窗被打开了,这条边沿也必须先走 dictation。否则 begin_qa_session 会
// 第二次抢同一个麦克风 device —— 在 Linux/PipeWire 上甚至会成功打开两路捕获,
// dictation 的 recorder 没人停;在 macOS/Windows 上 cpal 会拒绝第二次 build_input_stream
// 但 dictation session 仍在跑、用户找不到从 QA 面板停掉它的入口。审计 3.3.1。
let dictation_active = !matches!(inner.state.lock().phase, SessionPhase::Idle);
let panel_visible = inner.qa_state.lock().panel_visible;
if panel_visible {
if panel_visible && !dictation_active {
handle_qa_option_edge(inner).await;
} else {
handle_pressed(inner).await;
Expand Down Expand Up @@ -48,8 +54,12 @@ pub(super) async fn handle_released_edge(inner: &Arc<Inner>) {
let was_held = inner.hotkey_trigger_held.swap(false, Ordering::SeqCst);
if was_held {
// QA 浮窗可见时,Option 行为是 press-toggle(不分 hold/release),release 边沿忽略。
// 与 handle_pressed_edge 的路由对称:dictation session 在跑时 Pressed 已经被路由到
// dictation,那 Released 必须也路由到 dictation —— 否则 Hold 模式松开热键时
// end_session 不会触发,dictation 永远停不下来。审计 3.3.1。
let dictation_active = !matches!(inner.state.lock().phase, SessionPhase::Idle);
let panel_visible = inner.qa_state.lock().panel_visible;
if panel_visible {
if panel_visible && !dictation_active {
return;
}
handle_released(inner).await;
Expand Down
15 changes: 11 additions & 4 deletions openless-all/app/src-tauri/src/coordinator/qa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use tauri::Emitter;

use crate::coordinator_state::{initial_session_id, SessionId};
use crate::coordinator_state::{initial_session_id, SessionId, SessionPhase};
use crate::selection::SelectionContext;
use crate::types::CapsuleState;

Expand Down Expand Up @@ -86,9 +86,16 @@ pub(super) fn open_qa_panel(inner: &Arc<Inner>) {
state.selection = None;
state.front_app = capture_frontmost_app();
}
// 先把胶囊清干净,避免主听写上一次 Done 状态残留的 message/insertedChars
// 在 QA Done 阶段被 capsule UI 错误复用("已之一粘贴这个 0" 那种)。
emit_capsule(inner, CapsuleState::Idle, 0.0, 0, None, None);
// 主听写 phase 是 Idle 才需要 sweep capsule —— 这里的语义是清掉「上一次 dictation
// Done 状态残留」的 message / insertedChars,让 QA 自己的 capsule 状态从干净起跑
// (否则 capsule UI 会出现 "已粘贴这个 0" 之类把上一次 inserted_chars 错误复用的
// 显示)。但如果 dictation 当前正处于 Recording / Polishing / Inserting / Done toast
// 显示中,强行 emit Idle 会把用户没看完的反馈抹掉、或者把 Polishing 中的进度条
// 卡死。审计 3.3.4。
let dictation_idle = matches!(inner.state.lock().phase, SessionPhase::Idle);
if dictation_idle {
emit_capsule(inner, CapsuleState::Idle, 0.0, 0, None, None);
}
if let Some(app) = inner.app.lock().clone() {
crate::show_qa_window(&app, "idle");
let _ = app.emit_to(
Expand Down
Loading