use crate::{apps::App, guard, manage, proc, ui};
use anyhow::{Result, bail};
use dialoguer::MultiSelect;
use dialoguer::theme::ColorfulTheme;
use std::io::IsTerminal;
pub fn run(
list: manage::List,
apps: &[App],
table: &proc::Table,
current: &[String],
) -> Result<Vec<manage::Change>> {
if !std::io::stdin().is_terminal() || !std::io::stdout().is_terminal() {
bail!("`amph pick` needs an interactive terminal; use `amph add <name>` instead");
}
let candidates: Vec<&App> = apps
.iter()
.filter(|a| !a.nested)
.filter(|a| guard::protected_match(&a.identities()).is_none())
.filter(|a| a.rss > 0)
.collect();
if candidates.is_empty() {
ui::skipped("nothing eligible is running");
return Ok(Vec::new());
}
let labels: Vec<String> = candidates
.iter()
.map(|a| {
format!(
"{:<26} {:>9} {:>3} proc{}",
a.name.chars().take(26).collect::<String>(),
ui::bytes(a.rss),
table.tree(a.pid).len(),
if a.foreground { "" } else { " (background)" }
)
})
.collect();
let checked: Vec<bool> = candidates
.iter()
.map(|a| guard::any_matches(&a.identities(), current))
.collect();
println!();
let chosen = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt(format!("Space to toggle, Enter to save — {}", list.label()))
.items(&labels)
.defaults(&checked)
.interact_opt()?;
let Some(indices) = chosen else {
ui::skipped("cancelled; nothing was changed");
return Ok(Vec::new());
};
let selected: Vec<String> = indices
.iter()
.map(|&i| candidates[i].name.clone())
.collect();
let offered: Vec<String> = candidates.iter().map(|a| a.name.clone()).collect();
manage::set_within(list, &selected, &offered)
}