#[cfg(feature = "mcp")]
use crate::extras::mcp::McpClientManager;
use crate::session::Session;
use crate::sync_util::LockExt;
use crate::ui::panel_data::{ContextGauge, GitSnapshot, LeftPanelInfo};
use crate::ui::renderer::PanelData;
use crate::ui::sysload::SharedSysLoad;
const FOLD_WARN_PCT: u16 = 75;
pub(crate) fn build_left_panel_info(
session: &Session,
activity: &[String],
git: Option<GitSnapshot>,
) -> LeftPanelInfo {
let used = session.total_estimated_tokens;
let window = session.context_window;
let pct = ((used.saturating_mul(100)).checked_div(window).unwrap_or(0)).min(100) as u16;
LeftPanelInfo {
context: ContextGauge {
used,
window,
pct,
compactions: session.compactions.len(),
fold_soon: pct >= FOLD_WARN_PCT,
},
activity: activity.to_vec(),
git,
}
}
static PANEL_MODIFIED_CACHE: std::sync::Mutex<Option<(u64, std::path::PathBuf, Vec<String>)>> =
std::sync::Mutex::new(None);
pub(crate) fn panel_modified_cached(cwd: &std::path::Path) -> Vec<String> {
let v = crate::agent::tools::modified::version();
{
let guard = PANEL_MODIFIED_CACHE.lock_ignore_poison();
if let Some((cached_v, cached_cwd, cached_data)) = guard.as_ref()
&& *cached_v == v
&& cached_cwd.as_path() == cwd
{
return cached_data.clone();
}
}
let cwd_buf = cwd.to_path_buf();
let rendered: Vec<String> = crate::agent::tools::modified::recent(256)
.into_iter()
.map(|p| {
p.strip_prefix(&cwd_buf)
.map(|r| r.display().to_string())
.unwrap_or_else(|_| {
p.file_name()
.and_then(|n| n.to_str())
.map(String::from)
.unwrap_or_else(|| p.display().to_string())
})
})
.collect();
let mut guard = PANEL_MODIFIED_CACHE.lock_ignore_poison();
*guard = Some((v, cwd_buf, rendered.clone()));
rendered
}
pub(crate) fn build_panel_data(
session: &Session,
sysload: Option<&SharedSysLoad>,
#[cfg(feature = "mcp")] mcp_manager: Option<&McpClientManager>,
#[cfg(feature = "lsp")] lsp_manager: Option<&std::sync::Arc<crate::lsp::manager::LspManager>>,
) -> PanelData {
use std::path::Path;
#[cfg(feature = "mcp")]
let mcp: Vec<(String, bool)> = mcp_manager
.map(|m| {
m.connections_snapshot()
.into_iter()
.map(|(name, _conn)| (name, true))
.collect()
})
.unwrap_or_default();
#[cfg(not(feature = "mcp"))]
let mcp: Vec<(String, bool)> = Vec::new();
#[cfg(feature = "lsp")]
let lsp: Vec<(String, String, bool)> = lsp_manager
.map(|m| {
let cwd_path = Path::new(session.working_dir.as_str());
let shorten = |p: &Path| -> String {
p.strip_prefix(cwd_path)
.map(|r| r.display().to_string())
.unwrap_or_else(|_| {
p.file_name()
.and_then(|n| n.to_str())
.map(String::from)
.unwrap_or_else(|| p.display().to_string())
})
};
let mut all = Vec::new();
for (id, root) in m.active_servers() {
all.push((id, shorten(&root), true));
}
for (id, root) in m.broken_servers() {
all.push((id, shorten(&root), false));
}
all
})
.unwrap_or_default();
#[cfg(not(feature = "lsp"))]
let lsp: Vec<(String, String, bool)> = Vec::new();
let todos: Vec<(String, String)> = {
let list = crate::agent::tools::todo::TODO_LIST.lock_ignore_poison();
list.iter()
.take(8)
.map(|t| {
let status = match t.status.as_str() {
"in_progress" => "[~]",
"completed" => "[x]",
_ => "[ ]",
};
(status.to_string(), t.content.to_string())
})
.collect()
};
let cwd_path = Path::new(session.working_dir.as_str()).to_path_buf();
let modified = panel_modified_cached(&cwd_path);
PanelData {
mcp,
lsp,
todos,
modified,
sysload: sysload.map(|s| s.snapshot()),
}
}