bunflared 0.9.0

One command, one throwaway link for your localhost, several ports behind it. Chat with your client from the terminal, send them to a page, follow their cursor, catch their reactions 🔥 Or let your coding agent run the session over MCP.
use ratatui::Frame;
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::Modifier;
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;

use super::{focusable, highlight, scroll, status_color};
use crate::tui::App;
use crate::tui::fx;
use crate::tui::keys::Focus;
use crate::tui::traffic::{Replay, Row};

pub fn draw_log(app: &App, frame: &mut Frame, area: Rect) {
    let title = match app.traffic.filter.as_str() {
        "" => "requests".to_string(),
        filter => format!("requests · /{filter}"),
    };
    let block = focusable(app, &title, Focus::Log);
    let inner = block.inner(area);
    frame.render_widget(block, area);
    let theme = &app.theme;
    let rows: Vec<&Row> = app.traffic.shown().collect();
    if rows.is_empty() {
        let empty = if app.traffic.rows.is_empty() {
            "No visitors yet. Send the link to someone!".to_string()
        } else {
            format!(
                "Nothing matches /{}. / then esc shows all.",
                app.traffic.filter
            )
        };
        let waiting = Line::from(Span::styled(empty, theme.fg(fx::DIM))).centered();
        let [_, middle, _] = Layout::vertical([
            Constraint::Min(0),
            Constraint::Length(1),
            Constraint::Min(0),
        ])
        .areas(inner);
        frame.render_widget(Paragraph::new(waiting), middle);
        return;
    }
    let at = rows
        .iter()
        .position(|row| Some(row.n) == app.traffic.picked);
    let lines: Vec<Line> = rows
        .iter()
        .skip(scroll(at, inner.height as usize))
        .take(inner.height as usize)
        .map(|row| {
            let hit = &row.hit;
            let replayed = match row.replayed {
                None => Span::raw(""),
                Some(Replay::Pending) => Span::styled("↻ …  ", theme.fg(fx::DIM)),
                Some(Replay::Done { status, .. }) => Span::styled(
                    format!("↻ {status} "),
                    theme.fg(status_color(status)).add_modifier(Modifier::BOLD),
                ),
                Some(Replay::Failed) => Span::styled("↻ ✖   ", theme.fg(fx::RED)),
            };
            let line = Line::from(vec![
                Span::styled(format!("{} ", row.clock), theme.fg(fx::DIM)),
                Span::styled(format!("{:<7}", hit.method), theme.fg(fx::PURPLE)),
                Span::styled(
                    format!("{} ", hit.status),
                    theme
                        .fg(status_color(hit.status))
                        .add_modifier(Modifier::BOLD),
                ),
                replayed,
                Span::styled(format!("{:>5}ms ", hit.ms), theme.fg(fx::FG)),
                Span::styled(format!(":{:<5} ", hit.port), theme.fg(fx::DIM)),
                Span::styled(hit.path.clone(), theme.fg(fx::FG)),
            ]);
            highlight(line, Some(row.n) == app.traffic.picked)
        })
        .collect();
    frame.render_widget(Paragraph::new(lines), inner);
}