use super::run;
use crate::reply::agent::Agent;
use crate::ui::{Model, Notice, theme};
pub const READY: &str = "ready";
pub const PLAN: &str = "plan";
pub fn state(model: &Model) -> super::Run {
if let Some(notice) = &model.notice {
let ink = match notice {
Notice::Said(_) => theme::accent(theme::State::Annotation),
_ => theme::accent(theme::State::Error),
};
return run(notice.line(), ink);
}
match down(model) {
Some(why) => run(why, theme::accent(theme::State::Error)),
None => calm(model),
}
}
pub fn down(model: &Model) -> Option<String> {
let aim = model.aim.as_ref()?;
model
.roster
.iter()
.find(|chunk| chunk.channel.name == aim.channel)
.and_then(|chunk| match &chunk.held {
crate::ui::Held::Unheld(why) => Some(why.clone()),
_ => None,
})
}
pub fn calm(model: &Model) -> super::Run {
match super::selected(model) {
Some(row) => run(
format!(
"{} · {}",
row.state.label(),
crate::ui::convs::age(row.age_secs)
),
theme::state_ink(&row.state),
),
None => run(READY, theme::INK_WEAK),
}
}
pub fn named(model: &Model, sentence: &str) -> Vec<crate::ui::slash::Row> {
let mut out: Vec<crate::ui::slash::Row> = Vec::new();
for word in sentence.split_whitespace() {
let Some(word) = word.strip_prefix(crate::ui::slash::LEAD) else {
continue;
};
let word = word.trim_end_matches(|c: char| !c.is_alphanumeric());
if let Some(row) = super::door(model, word)
&& !out.contains(&row)
{
out.push(row);
}
}
out
}
pub fn doors(model: &Model) -> Vec<super::Run> {
let Some(notice) = &model.notice else {
return Vec::new();
};
named(model, ¬ice.line())
.into_iter()
.map(|row| super::Run {
door: Some(super::Door::Row(row)),
..run(
format!("{}{}", crate::ui::slash::LEAD, row.word),
theme::BRAND,
)
})
.collect()
}
pub fn count(n: u64) -> String {
match n {
0..10_000 => n.to_string(),
10_000..1_000_000 => format!("{}.{}k", n / 1000, n % 1000 / 100),
_ => format!("{}.{}M", n / 1_000_000, n % 1_000_000 / 100_000),
}
}
pub fn tokens(row: &Agent) -> Vec<String> {
let spent = &row.spend.tokens;
let mut out = vec![format!(
"{} in · {} out",
count(spent.input),
count(spent.output)
)];
if spent.cache_read + spent.cache_write > 0 {
out.push(format!(
"cache {} read · {} write",
count(spent.cache_read),
count(spent.cache_write)
));
}
out
}
pub fn diagnostics(model: &Model) -> Vec<super::Run> {
let mut out = Vec::new();
if model.plan {
out.push(run(PLAN, theme::BRAND));
}
out.push(state(model));
out.extend(doors(model));
let Some(row) = model.agent() else {
return out;
};
for said in tokens(row) {
out.push(run(said, theme::INK_WEAK));
}
if let Some(cost) = &row.spend.cost {
out.push(run(cost.said(), theme::INK_WEAK));
}
if let Some(full) = &row.context {
out.push(run(format!("context {}%", full.percent), theme::INK_WEAK));
}
out
}