0cea6a4b28
- 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>
85 lines
1.8 KiB
Rust
85 lines
1.8 KiB
Rust
mod audio_feedback;
|
|
mod cli;
|
|
mod config;
|
|
mod coordinator;
|
|
mod hotkey;
|
|
mod ipc;
|
|
mod model_cache;
|
|
mod overlay;
|
|
mod paste;
|
|
mod recorder;
|
|
mod shared_state;
|
|
mod transcriber;
|
|
mod vad;
|
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "mouth", version, about = "Offline speech-to-text with global hotkey and paste")]
|
|
struct Cli {
|
|
#[command(subcommand)]
|
|
command: Option<Commands>,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
/// Start the mouth daemon
|
|
Run,
|
|
|
|
/// View or edit configuration
|
|
Config {
|
|
/// Print current config to stdout
|
|
#[arg(long)]
|
|
show: bool,
|
|
|
|
/// Reset config to defaults
|
|
#[arg(long)]
|
|
reset: bool,
|
|
},
|
|
|
|
/// Manage speech-to-text models
|
|
Models {
|
|
/// Download the configured model
|
|
#[arg(long)]
|
|
download: bool,
|
|
},
|
|
|
|
/// Show daemon status, loaded model, and version
|
|
Status,
|
|
}
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
|
)
|
|
.init();
|
|
|
|
let cli = Cli::parse();
|
|
|
|
match cli.command {
|
|
None | Some(Commands::Run) => cli::run_cmd::run(),
|
|
|
|
Some(Commands::Config { show, reset }) => {
|
|
if show {
|
|
cli::config_cmd::show()
|
|
} else if reset {
|
|
cli::config_cmd::reset()
|
|
} else {
|
|
cli::config_cmd::interactive()
|
|
}
|
|
}
|
|
|
|
Some(Commands::Models { download }) => {
|
|
if download {
|
|
cli::models_cmd::download()
|
|
} else {
|
|
cli::models_cmd::list()
|
|
}
|
|
}
|
|
|
|
Some(Commands::Status) => cli::status_cmd::status(),
|
|
}
|
|
}
|