// thalamus_screen.rs — F5: attention routing and channel status // // Shows presence/idle/activity status, then channel status. // Channel data is cached on App and refreshed on screen entry. use ratatui::{ layout::Rect, style::{Color, Style}, text::{Line, Span}, widgets::{Block, Borders, Paragraph, Wrap}, Frame, }; use super::{App, SCREEN_LEGEND}; fn fetch_daemon_status() -> Vec { std::process::Command::new("poc-daemon") .arg("status") .output() .ok() .and_then(|o| { if o.status.success() { String::from_utf8(o.stdout).ok() } else { None } }) .map(|s| s.lines().map(String::from).collect()) .unwrap_or_default() } impl App { pub(crate) fn draw_thalamus(&self, frame: &mut Frame, size: Rect) { let section = Style::default().fg(Color::Yellow); let dim = Style::default().fg(Color::DarkGray); let mut lines: Vec = Vec::new(); // Presence status let daemon_status = fetch_daemon_status(); if !daemon_status.is_empty() { lines.push(Line::styled("── Presence ──", section)); lines.push(Line::raw("")); for line in &daemon_status { lines.push(Line::raw(format!(" {}", line))); } lines.push(Line::raw("")); } // Channel status from cached data lines.push(Line::styled("── Channels ──", section)); lines.push(Line::raw("")); if self.channel_status.is_empty() { lines.push(Line::styled(" no channels configured", dim)); } else { for ch in &self.channel_status { let (symbol, color) = if ch.connected { ("●", Color::Green) } else { ("○", Color::Red) }; let unread_str = if ch.unread > 0 { format!(" ({} unread)", ch.unread) } else { String::new() }; lines.push(Line::from(vec![ Span::raw(" "), Span::styled(symbol, Style::default().fg(color)), Span::raw(format!(" {:<24}", ch.name)), Span::styled( if ch.connected { "connected" } else { "disconnected" }, Style::default().fg(color), ), Span::styled(unread_str, Style::default().fg(Color::Yellow)), ])); } } let block = Block::default() .title_top(Line::from(SCREEN_LEGEND).left_aligned()) .title_top(Line::from(" thalamus ").right_aligned()) .borders(Borders::ALL) .border_style(Style::default().fg(Color::Cyan)); let para = Paragraph::new(lines) .block(block) .wrap(Wrap { trim: false }) .scroll((self.debug_scroll, 0)); frame.render_widget(para, size); } }