v0.2.0: System tray, IPC status, VAD, hotkey grab, and polish
- Add system tray icon with Exit menu (tray-icon/muda) - Add IPC daemon status via named pipe (Windows) / Unix socket (Linux) - Add `mouth status` command to query running daemon - Add daemon lock to prevent multiple instances - Hide Windows console window when running as daemon - Wire up Silero VAD model download and speech filtering - Switch hotkey listener from rdev::listen to rdev::grab to consume hotkeys - Add hotkey capture mode in interactive config (press keys instead of typing) - Add all missing key names (brackets, punctuation, numpad, etc.) - Fix ONNX tensor type mismatches (encoder wants i64, decoder wants i32) - Add 300ms lead-in silence to compensate for mic startup latency - Add 300ms trailing recording after stop for speech not to be clipped - Add 50ms silence before audio feedback blips for device warmup - Reduce overlay size (150x18, was 200x36) - Add PolyForm Noncommercial 1.0.0 license - Flesh out user-focused README - Update release script with Gitea/GitHub forge support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+37
-8
@@ -1,7 +1,9 @@
|
||||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::config::{Accelerator, Config, OverlayPosition, PasteMethod, RecordingMode};
|
||||
use crate::hotkey::capture_hotkey;
|
||||
|
||||
pub fn show() -> Result<()> {
|
||||
let config = Config::load()?;
|
||||
@@ -20,10 +22,7 @@ pub fn reset() -> Result<()> {
|
||||
pub fn interactive() -> Result<()> {
|
||||
let mut config = Config::load()?;
|
||||
|
||||
config.hotkey = Input::new()
|
||||
.with_prompt("Hotkey")
|
||||
.default(config.hotkey)
|
||||
.interact_text()?;
|
||||
config.hotkey = prompt_hotkey("Hotkey", &config.hotkey)?;
|
||||
|
||||
let mode_idx = Select::new()
|
||||
.with_prompt("Recording mode")
|
||||
@@ -38,10 +37,7 @@ pub fn interactive() -> Result<()> {
|
||||
_ => RecordingMode::Toggle,
|
||||
};
|
||||
|
||||
config.cancel_key = Input::new()
|
||||
.with_prompt("Cancel key")
|
||||
.default(config.cancel_key)
|
||||
.interact_text()?;
|
||||
config.cancel_key = prompt_hotkey("Cancel key", &config.cancel_key)?;
|
||||
|
||||
config.model = Input::new()
|
||||
.with_prompt("Model")
|
||||
@@ -125,3 +121,36 @@ pub fn interactive() -> Result<()> {
|
||||
println!("\nConfig saved to {}", Config::path()?.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prompt the user to either press a key combination or type it manually.
|
||||
fn prompt_hotkey(label: &str, current: &str) -> Result<String> {
|
||||
let choice = Select::new()
|
||||
.with_prompt(format!("{label} (current: {current})"))
|
||||
.items(&["Press the key combination", "Type it manually", "Keep current"])
|
||||
.default(0)
|
||||
.interact()?;
|
||||
|
||||
match choice {
|
||||
0 => {
|
||||
println!("Press your desired key combination (timeout: 10s)...");
|
||||
match capture_hotkey(Duration::from_secs(10)) {
|
||||
Some(hotkey) => {
|
||||
println!(" Captured: {hotkey}");
|
||||
Ok(hotkey)
|
||||
}
|
||||
None => {
|
||||
println!(" No keypress detected, keeping current: {current}");
|
||||
Ok(current.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
let value = Input::new()
|
||||
.with_prompt(label)
|
||||
.default(current.to_string())
|
||||
.interact_text()?;
|
||||
Ok(value)
|
||||
}
|
||||
_ => Ok(current.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user