use std::path::Path;
use crate::app::Snapshot;
use crate::attention;
use crate::git_tree::AgentState;
use crate::nav::ws_key;
use crate::ui_state::UiState;
use crate::boundary::reply::WsRow;
pub fn workspaces(
snap: &Snapshot,
ui: &UiState,
now_unix: i64,
) -> crate::boundary::reply::Workspaces {
let age = std::time::Duration::from_secs(
now_unix
.saturating_sub(snap.derived_at_unix)
.try_into()
.unwrap_or(0),
);
crate::boundary::reply::Workspaces {
rows: ws_rows(snap, ui),
stale: crate::app::stale_label(age, snap.cadence.stale_after()),
growth: crate::app::growth_label(&snap.growth),
}
}
pub fn ws_rows(snap: &Snapshot, ui: &UiState) -> Vec<WsRow> {
let pinned = ui.pinned();
let prices = ui.prices();
snap.workspaces
.iter()
.map(|w| {
let (attention, agents, running) = workspace_stats(snap, ui, &w.path);
let key = ws_key(&w.path);
WsRow {
workspace: crate::naming::leaf(&w.path),
kind: w.kind.clone(),
attention,
agents,
running,
pinned: pinned.iter().position(|k| *k == key),
config_tip: snap
.trees
.get(&w.path)
.and_then(|t| t.commits.last())
.map(|c| crate::model_pick::ConfigTip {
oid: c.oid.clone(),
short_oid: c.short_oid.clone(),
}),
spend: crate::spend::priced(
snap.bills.get(&w.path).map_or(&[][..], Vec::as_slice),
&prices,
),
}
})
.collect()
}
pub fn workspace_stats(snap: &Snapshot, ui: &UiState, ws: &Path) -> (usize, usize, bool) {
let Some(tree) = snap.trees.get(ws) else {
return (0, 0, false);
};
let key = ws_key(ws);
let seen = |k, w: &str, a: &str, o: &str| ui.is_seen(k, w, a, o);
let mut count = 0;
let mut running = false;
for a in &tree.agents {
if attention::attention(a, &tree.agents, &key, &seen).any() {
count += 1;
}
running |= matches!(a.state, AgentState::Live | AgentState::InFlight);
}
(count, tree.agents.len(), running)
}