Add input device selection to interactive config TUI

Enumerates available audio input devices via cpal and presents them
in a dropdown, with "System default" as the first option.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 09:21:44 +01:00
parent 3fa4d102df
commit daa1542672
+49
View File
@@ -1,4 +1,5 @@
use anyhow::Result; use anyhow::Result;
use cpal::traits::{DeviceTrait, HostTrait};
use dialoguer::{Input, Select}; use dialoguer::{Input, Select};
use std::time::Duration; use std::time::Duration;
@@ -105,6 +106,8 @@ pub fn interactive() -> Result<()> {
.interact()?; .interact()?;
config.audio_feedback = feedback_idx == 0; config.audio_feedback = feedback_idx == 0;
config.input_device = prompt_input_device(config.input_device.as_deref())?;
let vad_idx = Select::new() let vad_idx = Select::new()
.with_prompt("VAD (voice activity detection)") .with_prompt("VAD (voice activity detection)")
.items(&["enabled", "disabled"]) .items(&["enabled", "disabled"])
@@ -122,6 +125,52 @@ pub fn interactive() -> Result<()> {
Ok(()) Ok(())
} }
/// Prompt the user to select an audio input device from available devices.
fn prompt_input_device(current: Option<&str>) -> Result<Option<String>> {
let current_label = current.unwrap_or("system default");
let host = cpal::default_host();
let mut device_names: Vec<String> = Vec::new();
if let Ok(devices) = host.input_devices() {
for device in devices {
if let Ok(name) = device.name() {
device_names.push(name);
}
}
}
// Build selection list: "System default" first, then each detected device
let mut items: Vec<String> = vec!["System default".to_string()];
for name in &device_names {
items.push(name.clone());
}
// Find the default selection index
let default_idx = if let Some(cur) = current {
let cur_lower = cur.to_lowercase();
device_names
.iter()
.position(|n| n.to_lowercase().contains(&cur_lower))
.map(|i| i + 1) // offset by 1 for "System default"
.unwrap_or(0)
} else {
0
};
let sel = Select::new()
.with_prompt(format!("Input device (current: {current_label})"))
.items(&items)
.default(default_idx)
.interact()?;
if sel == 0 {
Ok(None)
} else {
Ok(Some(device_names[sel - 1].clone()))
}
}
/// Prompt the user to either press a key combination or type it manually. /// Prompt the user to either press a key combination or type it manually.
fn prompt_hotkey(label: &str, current: &str) -> Result<String> { fn prompt_hotkey(label: &str, current: &str) -> Result<String> {
let choice = Select::new() let choice = Select::new()