use crate::reply::convs::ConvRow;
use crate::reply::queue::{Held, QueueRow};
use crate::reply::roster::Workspaces;
use crate::reply::stream::{Delta, Stream};
use super::parts::{age, brief, clause, line, line_over, listing, quoted, tally, things, when};
pub(super) fn workspaces(roster: &Workspaces) -> String {
let rows = roster
.rows
.iter()
.map(|row| {
line(vec![
Some(row.workspace.clone()),
Some(row.kind.label()),
things(row.agents, "conversation"),
tally(row.attention, "waiting"),
when(row.running, "running"),
clause("pinned", row.pinned.map(|n| n.to_string()).as_deref()),
])
})
.collect();
let head = line(vec![
Some("workspaces".to_owned()),
clause("(stale:", roster.stale.as_deref()).map(|said| format!("{said})")),
clause("(growing:", roster.growth.as_deref()).map(|said| format!("{said})")),
]);
listing(
&head,
rows,
"none yet — `lernie start home \"<goal>\"` founds the first",
)
}
pub(super) fn conversations(rows: &[ConvRow]) -> String {
let painted = rows
.iter()
.map(|row| {
let head = line(vec![
Some(row.display.clone()),
Some(row.state.label()),
when(row.uncertain, "(uncertain)"),
Some(age(row.age_secs)),
things(row.members, "member"),
tally(row.depth, "deep"),
tally(row.attention, "waiting"),
clause("failed:", row.failure.as_deref().map(brief).as_deref()),
]);
line_over(&head, quoted(&row.preview))
})
.collect();
listing(
"conversations",
painted,
"none yet — `lernie start <workspace> \"<goal>\"` begins one",
)
}
pub(super) fn attention(rows: &[QueueRow]) -> String {
let painted = rows
.iter()
.map(|row| {
let head = line(vec![
Some(format!("{}/{}", row.workspace, row.display)),
Some(row.state.label()),
when(row.uncertain, "(uncertain)"),
Some(age(row.age_secs)),
tally(row.pending, "queued"),
when(!row.signals.is_empty(), &row.signals.join(", ")),
clause("flagged:", row.flag.as_ref().map(|f| f.reason.as_str())),
clause("failed:", row.failure.as_deref().map(brief).as_deref()),
]);
line_over(&head, row.held.as_ref().map(parked))
})
.collect();
listing("waiting on you", painted, "nothing is waiting on you")
}
pub(super) fn parked(held: &Held) -> String {
format!("held: {} ({}) — {}", held.tool, held.tool_use, held.reason)
}
pub(super) fn follow(stream: &Stream) -> String {
let landed = line(vec![
clause("(thinking)", stream.thinking.as_deref()),
stream.text.clone(),
]);
if !landed.is_empty() {
return landed;
}
line(vec![
Some("…".to_owned()),
stream.last_delta.as_ref().map(Delta::label),
])
}
pub(super) fn tail(thinking: &str, text: &str) -> String {
line(vec![
when(
!thinking.is_empty(),
&line_over("thinking", Some(thinking.to_owned())),
),
when(!text.is_empty(), text),
])
}