//! Overlay: usage statistics — detailed token usage, API call count, edit/ //! review/lesson activity counters, and session elapsed time. use crate::view::sidebar::compute_usage_summary; use crate::view::theme::Theme; use ratatui::style::{Modifier, Style}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Paragraph}; use ratatui::Frame; use tracing::debug; /// Render the Usage overlay. #[tracing::instrument(skip_all)] pub fn render( frame: &mut Frame, area: ratatui::layout::Rect, block: Block<'static>, state: &crate::state::AppStateRest, ) { let has_runtime = state.session_runtime.is_some(); debug!("Rendering Usage overlay, has runtime: {has_runtime}"); let block = block .title(Span::styled( " Usage ", Style::default() .fg(Theme::INFO) .add_modifier(Modifier::BOLD), )) .border_style(Style::default().fg(Theme::INFO)); let runtime = state.session_runtime.as_ref(); let now_ms = chrono::Utc::now().timestamp_millis(); let summary = runtime.map(|r| compute_usage_summary(&r.usage, r.session_start, now_ms)); let (edit_count, lesson_count, review_count, consec_empty) = runtime.map_or((0, 0, 0, 0), |r| { ( r.edit_count, r.lessons.total, r.review_count, r.consecutive_empty_reviews, ) }); let mut lines = vec![ Line::from(Span::styled( " Token Usage", Style::default() .fg(Theme::INFO) .add_modifier(Modifier::BOLD), )), Line::from(Span::raw("")), ]; if let Some(s) = &summary { lines.push(Line::from(Span::styled( format!(" Main agent: {} tokens", s.main_tokens), Style::default().fg(Theme::TEXT), ))); lines.push(Line::from(Span::styled( format!(" Self-learning: {} tokens", s.self_learning_tokens), Style::default().fg(Theme::TEXT_MUTED), ))); lines.push(Line::from(Span::styled( format!(" Total: {} tokens", s.total_tokens), Style::default() .fg(Theme::TEXT) .add_modifier(Modifier::BOLD), ))); lines.push(Line::from(Span::styled( format!(" API calls: {}", s.api_calls), Style::default().fg(Theme::TEXT), ))); } else { lines.push(Line::from(Span::styled( " No active session.", Style::default().fg(Theme::TEXT_DIM), ))); } lines.push(Line::from(Span::raw(""))); lines.push(Line::from(Span::styled( " Activity", Style::default() .fg(Theme::INFO) .add_modifier(Modifier::BOLD), ))); lines.push(Line::from(Span::styled( format!(" Edits: {edit_count}"), Style::default().fg(Theme::TEXT), ))); lines.push(Line::from(Span::styled( format!(" Reviews: {review_count}"), Style::default().fg(Theme::TEXT), ))); lines.push(Line::from(Span::styled( format!(" Lessons: {lesson_count}"), Style::default().fg(Theme::TEXT_MUTED), ))); lines.push(Line::from(Span::styled( format!(" Empty reviews: {consec_empty}"), Style::default().fg(if consec_empty > 3 { Theme::WARNING } else { Theme::TEXT_DIM }), ))); if let Some(s) = &summary { lines.push(Line::from(Span::raw(""))); lines.push(Line::from(Span::styled( format!( " Session: {}h {}m {}s", s.elapsed_hours, s.elapsed_minutes, s.elapsed_seconds ), Style::default().fg(Theme::TEXT_DIM), ))); } let paragraph = Paragraph::new(lines).block(block); frame.render_widget(paragraph, area); }