use ratatui::text::Line;
use crate::tui::app::App;
use crate::tui::sidebar::{self, SidebarSubagentSummary, WorkPanelOpts};
use crate::tui::subagent_routing::active_fanout_counts;
use super::model::RailPanel;
const NATURAL_HEIGHT_PROBE: usize = 64;
pub(crate) fn panel_lines(
app: &mut App,
panel: RailPanel,
content_width: usize,
max_rows: usize,
omit_goal_objective: bool,
) -> Option<Vec<Line<'static>>> {
let content_width = content_width.max(1);
let max_rows = max_rows.max(1);
match panel {
RailPanel::Tasks => None,
RailPanel::Agents => Some(agents_panel_lines(app, content_width, max_rows)),
RailPanel::Context => Some(sidebar::context_panel_lines(app, content_width)),
RailPanel::Pinned => Some(pinned_panel_lines(
app,
content_width,
max_rows,
omit_goal_objective,
)),
}
}
pub(crate) fn panel_has_useful_content(app: &mut App, panel: RailPanel) -> bool {
match panel {
RailPanel::Tasks => true,
RailPanel::Pinned => sidebar::sidebar_work_summary(app).has_useful_content(),
RailPanel::Agents => agents_have_useful_content(app),
RailPanel::Context => true,
}
}
pub(crate) fn panel_content_row_count(
app: &mut App,
panel: RailPanel,
content_width: usize,
omit_goal_objective: bool,
) -> usize {
panel_lines(
app,
panel,
content_width,
NATURAL_HEIGHT_PROBE,
omit_goal_objective,
)
.map(|lines| lines.len())
.unwrap_or(0)
}
fn agents_have_useful_content(app: &App) -> bool {
if !app.subagent_cache.is_empty() {
return true;
}
if !app.agent_progress.is_empty() {
return true;
}
if active_fanout_counts(app).is_some_and(|(_, total)| total > 0) {
return true;
}
sidebar::foreground_rlm_running(app)
}
fn agents_panel_lines(app: &App, content_width: usize, max_rows: usize) -> Vec<Line<'static>> {
let cached_ids: std::collections::HashSet<&str> = app
.subagent_cache
.iter()
.map(|agent| agent.agent_id.as_str())
.collect();
let progress_only_count = app
.agent_progress
.keys()
.filter(|id| !cached_ids.contains(id.as_str()))
.count();
let cached_running = app
.subagent_cache
.iter()
.filter(|agent| sidebar::cached_agent_activity_is_live(app, agent))
.count();
let role_counts: std::collections::BTreeMap<String, usize> =
app.subagent_cache
.iter()
.fold(std::collections::BTreeMap::new(), |mut acc, agent| {
*acc.entry(agent.agent_type.as_str().to_string())
.or_insert(0) += 1;
acc
});
let (fanout_running, fanout_total) = active_fanout_counts(app)
.map(|(running, total)| (running, Some(total)))
.unwrap_or((0, None));
let summary = SidebarSubagentSummary {
cached_total: app.subagent_cache.len(),
cached_running,
progress_only_count,
fanout_total,
fanout_running,
foreground_rlm_running: sidebar::foreground_rlm_running(app),
role_counts,
};
let rows = sidebar::sidebar_agent_rows(app);
sidebar::subagent_panel_lines(
&summary,
&rows,
app.ui_locale,
content_width,
max_rows,
&app.ui_theme,
)
}
fn pinned_panel_lines(
app: &mut App,
content_width: usize,
max_rows: usize,
omit_goal_objective: bool,
) -> Vec<Line<'static>> {
let summary = sidebar::sidebar_work_summary(app);
sidebar::work_panel_lines_with_opts(
&summary,
content_width,
max_rows,
app.ui_theme.mode,
&app.ui_theme,
WorkPanelOpts {
omit_goal_objective,
},
)
}