thd75_tui/ui/
mcp.rs

1use ratatui::Frame;
2use ratatui::layout::{Constraint, Direction, Layout, Rect};
3use ratatui::style::{Color, Style};
4use ratatui::widgets::{Block, Borders, Gauge, Paragraph};
5
6use crate::app::{App, McpState, Pane};
7
8pub(crate) fn render(app: &App, frame: &mut Frame<'_>, list_area: Rect, detail_area: Rect) {
9    let block = Block::default()
10        .title(" MCP Programming ")
11        .borders(Borders::ALL)
12        .border_style(super::border_style(app, Pane::Main));
13
14    match &app.mcp {
15        McpState::Idle => {
16            let text = " Press [r] to read memory from radio\n Press [w] to write memory to radio";
17            frame.render_widget(Paragraph::new(text).block(block), list_area);
18        }
19        McpState::Reading { page, total } | McpState::Writing { page, total } => {
20            let label = if matches!(app.mcp, McpState::Reading { .. }) {
21                "Reading"
22            } else {
23                "Writing"
24            };
25            let pct = if *total > 0 {
26                (f64::from(*page) / f64::from(*total)).min(1.0)
27            } else {
28                0.0
29            };
30
31            let inner = Layout::default()
32                .direction(Direction::Vertical)
33                .constraints([Constraint::Length(3), Constraint::Min(1)])
34                .split(block.inner(list_area));
35
36            frame.render_widget(block, list_area);
37
38            let gauge = Gauge::default()
39                .block(Block::default().title(format!(" {label} ")))
40                .gauge_style(Style::default().fg(Color::Cyan))
41                .ratio(pct)
42                .label(format!("{}/{} ({:.0}%)", page, total, pct * 100.0));
43            frame.render_widget(gauge, inner[0]);
44        }
45        McpState::Loaded { modified, .. } => {
46            let status = if *modified { " (modified)" } else { "" };
47            let text = format!(
48                " Memory loaded{status}\n\n Press [r] to re-read\n Press [w] to write to radio\n\n Switch to [c]hannels to browse"
49            );
50            frame.render_widget(Paragraph::new(text).block(block), list_area);
51        }
52        McpState::Reconnecting => {
53            let text = " Reconnecting to radio after MCP write...\n Waiting for USB device...";
54            frame.render_widget(Paragraph::new(text).block(block), list_area);
55        }
56    }
57
58    // Detail pane: empty for MCP view
59    let detail_block = Block::default()
60        .title(" MCP Info ")
61        .borders(Borders::ALL)
62        .border_style(super::border_style(app, Pane::Detail));
63    frame.render_widget(Paragraph::new("").block(detail_block), detail_area);
64}