use ratatui::{prelude::*, widgets::*};
use crate::models::Header;
pub fn render_input<'a>(
content: &'a str,
title: &'a str,
is_editing: bool,
is_focused: bool,
base_color: Color,
wrap: bool,
) -> Paragraph<'a> {
let border_style = if is_editing {
Style::default().fg(Color::Yellow)
} else if is_focused {
Style::default().fg(base_color)
} else {
Style::default()
};
let block = Block::default()
.borders(Borders::ALL)
.border_style(border_style)
.title(title);
let p = Paragraph::new(content).block(block);
if wrap {
p.wrap(Wrap { trim: false })
} else {
p
}
}
pub fn render_header_list<'a>(
headers: &'a [Header],
title: &'a str,
selected: usize,
is_focused: bool,
) -> List<'a> {
let items: Vec<ListItem> = headers
.iter()
.enumerate()
.map(|(i, h)| {
let style = if !h.enabled {
Style::default().fg(Color::DarkGray)
} else if is_focused && i == selected {
Style::default().fg(Color::Yellow).bold()
} else {
Style::default()
};
let prefix = if h.enabled { "[x]" } else { "[ ]" };
ListItem::new(format!("{} {}: {}", prefix, h.key, h.value)).style(style)
})
.collect();
let border_style = if is_focused {
Style::default().fg(Color::Cyan)
} else {
Style::default()
};
List::new(items).block(
Block::default()
.borders(Borders::ALL)
.border_style(border_style)
.title(title),
)
}
pub fn render_tabs<'a>(titles: &[&'a str], selected: usize) -> Tabs<'a> {
let titles: Vec<Line> = titles.iter().map(|t| Line::from(*t)).collect();
Tabs::new(titles)
.select(selected)
.style(Style::default().fg(Color::DarkGray))
.highlight_style(Style::default().fg(Color::Yellow).bold())
.divider("|")
}
#[allow(unused_mut)]
pub fn highlight_json(text: &str) -> Vec<Line<'static>> {
let mut lines = Vec::new();
for line in text.lines() {
let mut spans = Vec::new();
let mut current = String::new();
let mut in_string = false;
let mut is_key = false;
for c in line.chars() {
match c {
'"' => {
if !current.is_empty() {
spans.push(Span::raw(current.clone()));
current.clear();
}
if in_string {
current.push(c);
let color = if is_key { Color::Cyan } else { Color::Green };
spans.push(Span::styled(current.clone(), Style::default().fg(color)));
current.clear();
in_string = false;
is_key = false;
} else {
in_string = true;
current.push(c);
is_key = line[line.find('"').unwrap_or(0)..].contains("\":");
}
}
':' if !in_string => {
if !current.is_empty() {
spans.push(Span::raw(current.clone()));
current.clear();
}
spans.push(Span::styled(":", Style::default().fg(Color::White)));
}
'{' | '}' | '[' | ']' if !in_string => {
if !current.is_empty() {
spans.push(Span::raw(current.clone()));
current.clear();
}
spans.push(Span::styled(
c.to_string(),
Style::default().fg(Color::Yellow),
));
}
'0'..='9' | '-' | '.' if !in_string => {
if !current.is_empty()
&& !current
.chars()
.all(|x| x.is_ascii_digit() || x == '-' || x == '.')
{
spans.push(Span::raw(current.clone()));
current.clear();
}
current.push(c);
}
't' | 'r' | 'u' | 'e' | 'f' | 'a' | 'l' | 's' | 'n' if !in_string => {
current.push(c);
if current == "true" || current == "false" || current == "null" {
spans.push(Span::styled(
current.clone(),
Style::default().fg(Color::Magenta),
));
current.clear();
}
}
_ => {
current.push(c);
}
}
}
if !current.is_empty() {
if current
.chars()
.all(|c| c.is_ascii_digit() || c == '-' || c == '.')
{
spans.push(Span::styled(current, Style::default().fg(Color::Yellow)));
} else {
spans.push(Span::raw(current));
}
}
lines.push(Line::from(spans));
}
lines
}
pub fn status_color(code: u16) -> Color {
match code {
200..=299 => Color::Green,
300..=399 => Color::Cyan,
400..=499 => Color::Red,
500..=599 => Color::Magenta,
_ => Color::Yellow,
}
}
pub fn method_color(method: &str) -> Color {
match method {
"GET" => Color::Green,
"POST" => Color::Yellow,
"PUT" => Color::Blue,
"PATCH" => Color::Cyan,
"DELETE" => Color::Red,
_ => Color::White,
}
}