Skip to main content

cli_tutor/ui/
free_practice_view.rs

1// free_practice.VIEW.1 — sandbox view with no expected-output matching
2use crate::app::{App, SubmitState};
3use crate::ui::input_bar;
4use ratatui::{
5    layout::{Constraint, Direction, Layout, Rect},
6    style::{Color, Modifier, Style},
7    text::{Line, Span},
8    widgets::{Block, Borders, Paragraph, Wrap},
9    Frame,
10};
11
12pub fn render(app: &App, frame: &mut Frame, area: Rect) {
13    let nc = app.config.no_color;
14
15    let chunks = Layout::default()
16        .direction(Direction::Vertical)
17        .constraints([
18            Constraint::Length(3), // header info
19            Constraint::Length(3), // input bar
20            Constraint::Min(4),    // output
21        ])
22        .split(area);
23
24    // free_practice.VIEW.2 — informational header
25    let header = Paragraph::new(Line::from(vec![
26        Span::styled(
27            "Free Practice",
28            crate::ui::s(
29                Style::default()
30                    .fg(Color::Cyan)
31                    .add_modifier(Modifier::BOLD),
32                nc,
33            ),
34        ),
35        Span::raw("  — type any command and press Enter. No expected output is checked."),
36    ]))
37    .block(Block::default().borders(Borders::ALL));
38    frame.render_widget(header, chunks[0]);
39
40    // free_practice.VIEW.3 — shared input bar
41    input_bar::render(app, frame, chunks[1]);
42
43    // free_practice.VIEW.4 — output panel
44    let (output_text, output_style) = match app.submit_state {
45        SubmitState::Error => (
46            app.last_output
47                .as_ref()
48                .map(|o| format!("Error: {}", o.stderr))
49                .unwrap_or_else(|| "Error".to_string()),
50            crate::ui::s(Style::default().fg(Color::Yellow), nc),
51        ),
52        _ => {
53            let text = app
54                .last_output
55                .as_ref()
56                .map(|o| {
57                    if o.timed_out {
58                        "Command timed out after 3s".to_string()
59                    } else if !o.stderr.is_empty() && o.stdout.is_empty() {
60                        format!("stderr: {}", o.stderr)
61                    } else {
62                        o.stdout.clone()
63                    }
64                })
65                .unwrap_or_default();
66            (text, Style::default())
67        }
68    };
69
70    let output_para = Paragraph::new(output_text)
71        .block(Block::default().borders(Borders::ALL).title("Output"))
72        .style(output_style)
73        .wrap(Wrap { trim: false })
74        .scroll((app.output_scroll, 0));
75    frame.render_widget(output_para, chunks[2]);
76}