Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/app/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ mod tests {
status_message: None,
status_set_at: None,

// JSON popup defaults
json_popup_open: false,
json_popup_content: String::new(),

saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down
4 changes: 4 additions & 0 deletions src/app/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ mod tests {
status_message: None,
status_set_at: None,

// JSON popup defaults
json_popup_open: false,
json_popup_content: String::new(),

saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down
28 changes: 28 additions & 0 deletions src/app/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ impl App {
return Ok(());
}

// JSON popup has highest priority once open.
if self.json_popup_open {
match key_event.code {
KeyCode::Esc | KeyCode::Char('q') => {
self.close_json_popup();
}
_ => {}
}
return Ok(());
}

match key_event.code {
// q should NOT quit while editing or while group search is active
KeyCode::Char('q') if !self.editing && !self.group_search_active => {
Expand Down Expand Up @@ -149,6 +160,19 @@ impl App {
self.copy_results_to_clipboard();
}

// Open JSON popup for current Results line (Results pane, not editing)
KeyCode::Char('j') if !self.editing && self.focus == Focus::Results => {
if let Some(line) = self.lines.get(self.results_scroll).cloned() {
// Heuristic: try to find a JSON object/array starting point
let msg_part = line
.find('{')
.or_else(|| line.find('['))
.map(|idx| &line[idx..])
.unwrap_or(line.as_str());
self.open_json_popup(msg_part);
}
}

// Toggle tail mode
KeyCode::Char('t') if !self.editing && !self.group_search_active => {
self.tail_mode = !self.tail_mode;
Expand Down Expand Up @@ -252,6 +276,10 @@ mod tests {
status_message: None,
status_set_at: None,

// JSON popup defaults
json_popup_open: false,
json_popup_content: String::new(),

saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down
30 changes: 30 additions & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ pub struct App {
pub status_message: Option<String>,
pub status_set_at: Option<Instant>,

// JSON popup state for viewing pretty-printed JSON logs
pub json_popup_open: bool,
pub json_popup_content: String,

pub saved_filters: Vec<SavedFilter>,

pub save_filter_popup_open: bool,
Expand All @@ -90,6 +94,29 @@ pub struct App {
}

impl App {
/// Open the JSON popup with pretty-printed content, if `raw` parses as JSON.
/// Falls back to the original string on parse/format errors.
pub fn open_json_popup(&mut self, raw: &str) {
// Try to parse as JSON first
if let Ok(value) = serde_json::from_str::<serde_json::Value>(raw) {
if let Ok(pretty) = serde_json::to_string_pretty(&value) {
self.json_popup_content = pretty;
} else {
self.json_popup_content = raw.to_string();
}
} else {
// Not valid JSON; just show the raw string
self.json_popup_content = raw.to_string();
}
self.json_popup_open = true;
}

/// Close the JSON popup, if open.
pub fn close_json_popup(&mut self) {
self.json_popup_open = false;
self.json_popup_content.clear();
}

pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
while !self.exit {
if self.focus == Focus::Filter && self.editing {
Expand Down Expand Up @@ -530,6 +557,9 @@ mod tests {
status_message: None,
status_set_at: None,

json_popup_open: false,
json_popup_content: String::new(),

saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ fn main() -> io::Result<()> {
status_message: None,
status_set_at: None,

json_popup_open: false,
json_popup_content: String::new(),
saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down
47 changes: 47 additions & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,49 @@ impl Widget for &App {
// Call the refactored renderer
self.render_results(results_inner, buf);

// JSON popup overlay (on top of main UI)
if self.json_popup_open {
// Centered popup: 70% width and height
let popup_area = Layout::vertical([
Constraint::Percentage(15),
Constraint::Percentage(70),
Constraint::Percentage(15),
])
.split(area)[1];

let popup_chunks = Layout::horizontal([
Constraint::Percentage(15),
Constraint::Percentage(70),
Constraint::Percentage(15),
])
.split(popup_area);

let popup_rect = popup_chunks[1];

// Fill popup area with a solid background, similar to other popups
let popup_bg = Style::default().bg(Color::Rgb(10, 10, 10)).fg(Color::White);
for y in popup_rect.y..popup_rect.y + popup_rect.height {
for x in popup_rect.x..popup_rect.x + popup_rect.width {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_char(' ').set_style(popup_bg);
}
}
}

let popup_block = Block::bordered()
.title("JSON")
.style(popup_bg)
.border_style(Style::default().fg(Color::Yellow));

let popup_inner = popup_block.inner(popup_rect);
popup_block.render(popup_rect, buf);

ratatui::widgets::Paragraph::new(self.json_popup_content.as_str())
.wrap(ratatui::widgets::Wrap { trim: false })
.style(popup_bg)
.render(popup_inner, buf);
}

let mut row_y = filter_inner.y;

let field_style =
Expand Down Expand Up @@ -558,6 +601,10 @@ mod ui_tests {
status_message: None,
status_set_at: None,

// JSON popup defaults
json_popup_open: false,
json_popup_content: String::new(),

saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down
6 changes: 5 additions & 1 deletion src/ui/results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,15 @@ mod tests {
results_scroll: 0,

tail_mode: false,
tail_stop: Arc::new(AtomicBool::new(false)),
tail_stop: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),

status_message: None,
status_set_at: None,

// JSON popup defaults
json_popup_open: false,
json_popup_content: String::new(),

saved_filters: Vec::new(),
save_filter_popup_open: false,
save_filter_name: String::new(),
Expand Down