pub mod apps_table;
pub mod beans_table;
pub mod dashboard;
pub mod describe;
pub mod dialog;
pub mod dumps_table;
pub mod edit_logger;
pub mod endpoints_table;
pub mod env_table;
pub mod header;
pub mod loggers_table;
pub mod mappings_table;
pub mod new_project;
pub mod resources;
pub mod server_dialog;
pub mod splash;
pub mod thread_viz;
use ratatui::{
layout::{Constraint, Direction, Layout},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::Paragraph,
Frame,
};
use crate::app::{App, Mode};
const SPRING_GREEN: Color = Color::Rgb(80, 200, 50);
pub fn highlight_text(text: &str, filter: &str, base_style: Style) -> Line<'static> {
if filter.is_empty() {
return Line::from(Span::styled(text.to_string(), base_style));
}
let hl_style = base_style
.fg(Color::Black)
.bg(Color::Yellow)
.add_modifier(Modifier::BOLD);
let lower_text = text.to_lowercase();
let lower_filter = filter.to_lowercase();
let mut spans: Vec<Span<'static>> = Vec::new();
let mut last_end = 0;
for (start, _) in lower_text.match_indices(&lower_filter) {
if start > last_end {
spans.push(Span::styled(text[last_end..start].to_string(), base_style));
}
spans.push(Span::styled(
text[start..start + lower_filter.len()].to_string(),
hl_style,
));
last_end = start + lower_filter.len();
}
if last_end < text.len() {
spans.push(Span::styled(text[last_end..].to_string(), base_style));
}
if spans.is_empty() {
return Line::from(Span::styled(text.to_string(), base_style));
}
Line::from(spans)
}
pub fn render(f: &mut Frame, app: &App) {
match app.mode {
Mode::Splash => {
splash::render(f, &app.splash_state);
return;
}
Mode::ServerDialog => {
render_main(f, app);
server_dialog::render(f, &app.server_dialog_state);
return;
}
Mode::NewProject => {
render_main(f, app);
new_project::render(f, &app.new_project_state);
return;
}
_ => {}
}
render_main(f, app);
match app.mode {
Mode::Confirm => {
dialog::render(f, app);
}
Mode::ErrorModal => {
dialog::render(f, app);
}
Mode::Resources => {
resources::render(f, app);
}
Mode::EditLogger => {
edit_logger::render(f, &app.edit_logger_state);
}
_ => {}
}
}
fn render_main(f: &mut Frame, app: &App) {
let size = f.area();
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(6), Constraint::Min(1), Constraint::Length(1), ])
.split(size);
let header_area = layout[0];
let content_area = layout[1];
let footer_area = layout[2];
header::render(f, app, header_area);
match app.mode {
Mode::Describe => {
describe::render(f, app, content_area);
}
Mode::ThreadViz => {
thread_viz::render(f, app, content_area);
}
_ => match app.active_resource.as_str() {
"apps" => apps_table::render(f, app, content_area),
"dashboard" => dashboard::render(f, app, content_area),
"endpoints" => endpoints_table::render(f, app, content_area),
"beans" => beans_table::render(f, app, content_area),
"loggers" => loggers_table::render(f, app, content_area),
"mappings" => mappings_table::render(f, app, content_area),
"env" => env_table::render(f, app, content_area),
"threaddump" => dumps_table::render_thread_dumps(f, app, content_area),
"heapdump" => dumps_table::render_heap_dumps(f, app, content_area),
_ => apps_table::render(f, app, content_area),
},
}
render_footer(f, app, footer_area);
}
fn render_footer(f: &mut Frame, app: &App, area: ratatui::layout::Rect) {
let footer_spans = if app.filter_active {
vec![
Span::styled(
" Filter: ",
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
),
Span::styled(&app.filter_text, Style::default().fg(Color::White)),
Span::styled(
"█",
Style::default().fg(Color::White), ),
]
} else {
let resource_label = app.active_resource.as_str();
let count = match resource_label {
"apps" => app.apps.len(),
"endpoints" => app.endpoints.len(),
"beans" => app.beans.len(),
"loggers" => app.loggers.len(),
"mappings" => app.mappings.len(),
"env" => app.env_props.len(),
"threaddump" => app.saved_thread_dumps.len(),
"heapdump" => app.saved_heap_dumps.len(),
_ => 0,
};
let mut spans = vec![Span::styled(
format!(" {} ", resource_label),
Style::default()
.fg(Color::Black)
.bg(SPRING_GREEN)
.add_modifier(Modifier::BOLD),
)];
if resource_label == "dashboard" {
spans.push(Span::styled(
format!(" {} ", app.current_server_name()),
Style::default().fg(Color::DarkGray),
));
spans.push(Span::styled(
" [R] Refresh [:] Commands ",
Style::default().fg(Color::DarkGray),
));
} else {
spans.push(Span::styled(
format!(" {} items", count),
Style::default().fg(Color::DarkGray),
));
spans.push(Span::styled(
" Press ':' for commands ",
Style::default().fg(Color::DarkGray),
));
}
if resource_label == "heapdump" {
spans.push(Span::styled(
" [v] VisualVM [m] Eclipse MAT [d] Describe ",
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
));
} else if resource_label == "threaddump" {
spans.push(Span::styled(
" [v] VisualVM [d] Describe ",
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
));
}
spans
};
let footer_line = Line::from(footer_spans);
let footer_widget = Paragraph::new(footer_line).style(Style::default().bg(Color::Black));
f.render_widget(footer_widget, area);
}