Skip to content

Commit a6ef9ae

Browse files
committed
feat: Add push-to-talk recording mode (#13)
Implements push-to-talk functionality alongside existing toggle mode: - Add recording mode selector in settings (Toggle vs Push-to-Talk) - Support press/release events for held-key recording - Update backend to handle ShortcutState::Pressed and Released events - Add visual mode indicators in settings UI - Update recording pill to show mode-specific text - Maintain backward compatibility with existing toggle users Technical implementation: - Use tauri-plugin-global-shortcut's press/release event support - Track PTT key state with atomic boolean - Handle mode switching dynamically without app restart - Cross-platform compatible (Windows, macOS, Linux) Closes #13
1 parent 6938472 commit a6ef9ae

File tree

6 files changed

+462
-79
lines changed

6 files changed

+462
-79
lines changed

src-tauri/src/commands/settings.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ pub struct Settings {
2121
pub compact_recording_status: bool,
2222
pub check_updates_automatically: bool,
2323
pub selected_microphone: Option<String>,
24+
// Push-to-talk support
25+
pub recording_mode: String, // "toggle" or "push_to_talk"
26+
pub use_different_ptt_key: bool,
27+
pub ptt_hotkey: Option<String>,
2428
}
2529

2630
impl Default for Settings {
@@ -38,6 +42,9 @@ impl Default for Settings {
3842
compact_recording_status: true, // Default to compact mode
3943
check_updates_automatically: true, // Default to automatic updates enabled
4044
selected_microphone: None, // Default to system default microphone
45+
recording_mode: "toggle".to_string(), // Default to toggle mode for backward compatibility
46+
use_different_ptt_key: false, // Default to using same key
47+
ptt_hotkey: Some("Alt+Space".to_string()), // Default PTT key
4148
}
4249
}
4350
}
@@ -102,6 +109,17 @@ pub async fn get_settings(app: AppHandle) -> Result<Settings, String> {
102109
selected_microphone: store
103110
.get("selected_microphone")
104111
.and_then(|v| v.as_str().map(|s| s.to_string())),
112+
recording_mode: store
113+
.get("recording_mode")
114+
.and_then(|v| v.as_str().map(|s| s.to_string()))
115+
.unwrap_or_else(|| Settings::default().recording_mode),
116+
use_different_ptt_key: store
117+
.get("use_different_ptt_key")
118+
.and_then(|v| v.as_bool())
119+
.unwrap_or_else(|| Settings::default().use_different_ptt_key),
120+
ptt_hotkey: store
121+
.get("ptt_hotkey")
122+
.and_then(|v| v.as_str().map(|s| s.to_string())),
105123
};
106124

107125
// Pill position is already loaded from store, no need for duplicate state
@@ -144,13 +162,71 @@ pub async fn save_settings(app: AppHandle, settings: Settings) -> Result<(), Str
144162
);
145163
store.set("selected_microphone", json!(settings.selected_microphone));
146164

165+
// Save push-to-talk settings
166+
store.set("recording_mode", json!(settings.recording_mode.clone()));
167+
store.set("use_different_ptt_key", json!(settings.use_different_ptt_key));
168+
if let Some(ref ptt_hotkey) = settings.ptt_hotkey {
169+
store.set("ptt_hotkey", json!(ptt_hotkey));
170+
}
171+
147172
// Save pill position if provided
148173
if let Some((x, y)) = settings.pill_position {
149174
store.set("pill_position", json!([x, y]));
150175
}
151176

152177
store.save().map_err(|e| e.to_string())?;
153178

179+
// Update recording mode in AppState
180+
let app_state = app.state::<crate::AppState>();
181+
let recording_mode = match settings.recording_mode.as_str() {
182+
"push_to_talk" => crate::RecordingMode::PushToTalk,
183+
_ => crate::RecordingMode::Toggle,
184+
};
185+
186+
if let Ok(mut mode_guard) = app_state.recording_mode.lock() {
187+
*mode_guard = recording_mode;
188+
log::info!("Recording mode updated to: {:?}", recording_mode);
189+
}
190+
191+
// Handle PTT shortcut registration if needed
192+
if recording_mode == crate::RecordingMode::PushToTalk && settings.use_different_ptt_key {
193+
if let Some(ptt_hotkey) = settings.ptt_hotkey.clone() {
194+
let normalized_ptt = crate::commands::key_normalizer::normalize_shortcut_keys(&ptt_hotkey);
195+
196+
if let Ok(ptt_shortcut) = normalized_ptt.parse::<tauri_plugin_global_shortcut::Shortcut>() {
197+
let shortcuts = app.global_shortcut();
198+
199+
// Unregister old PTT shortcut if exists
200+
if let Ok(ptt_guard) = app_state.ptt_shortcut.lock() {
201+
if let Some(old_ptt) = ptt_guard.clone() {
202+
let _ = shortcuts.unregister(old_ptt);
203+
}
204+
}
205+
206+
// Register new PTT shortcut
207+
match shortcuts.register(ptt_shortcut.clone()) {
208+
Ok(_) => {
209+
if let Ok(mut ptt_guard) = app_state.ptt_shortcut.lock() {
210+
*ptt_guard = Some(ptt_shortcut);
211+
}
212+
log::info!("PTT shortcut updated to: {}", ptt_hotkey);
213+
}
214+
Err(e) => {
215+
log::error!("Failed to register PTT shortcut: {}", e);
216+
}
217+
}
218+
}
219+
}
220+
} else {
221+
// Clear PTT shortcut if not using different key
222+
if let Ok(mut ptt_guard) = app_state.ptt_shortcut.lock() {
223+
if let Some(old_ptt) = ptt_guard.clone() {
224+
let _ = app.global_shortcut().unregister(old_ptt);
225+
}
226+
*ptt_guard = None;
227+
}
228+
}
229+
154230
// Preload new model and update tray menu if model changed
155231
if !settings.current_model.is_empty() && old_model != settings.current_model {
156232
use crate::commands::model::preload_model;

0 commit comments

Comments
 (0)