better span view

This commit is contained in:
Jana Dönszelmann 2026-04-02 10:40:55 +02:00
parent 43e40b61e3
commit fdfc08e88b
No known key found for this signature in database
12 changed files with 612 additions and 206 deletions

View file

@ -7,6 +7,7 @@ use crossterm::{
};
use ratatui_themes::{Color, Theme, ThemeName};
use std::{
borrow::Cow,
fs::{self, DirEntry},
io::{self, Stdout},
ops::ControlFlow,
@ -15,7 +16,6 @@ use std::{
sync::Arc,
time::Duration,
};
use tui_widget_list::{ListBuilder, ListView};
use crate::tui::{
filter::{Filter, FilterKind, Matcher},
@ -24,7 +24,10 @@ use crate::tui::{
input::{FieldMatcher, InputState, InputTarget},
},
reader::LogfileReader,
widgets::{hyperlink::Hyperlink, items::Items, last_error::LastError, styled::IntoStyled},
widgets::{
fieldtree::FieldTree, hyperlink::Hyperlink, items::Items, last_error::LastError,
styled::IntoStyled,
},
};
use ratatui::{
DefaultTerminal, Terminal,
@ -33,6 +36,7 @@ use ratatui::{
layout::{Constraint, HorizontalAlignment, Layout, Rect},
prelude::CrosstermBackend,
style::Style,
symbols::merge::MergeStrategy,
text::Line,
widgets::{
Block, Clear, List, ListItem, ListState, Padding, Paragraph, StatefulWidget, Widget, Wrap,
@ -59,12 +63,16 @@ const HELP_TEXT: &str = "Generic:
<- / backspace / h exit nested view
-> / enter / l enter nested view
f toggle show active filters
targeting logs:
f fields
t the selected log
s surrounding element
tab switch to fields display
(to target values of fields)
t the currently selected log
the surrounding element (when inside a span)
either a field after `f` or the text of the current log:
p ... with a prefix
@ -314,15 +322,14 @@ impl App {
KeyCode::Char('G') | KeyCode::Home => lv.home(),
KeyCode::Char('g') | KeyCode::End => todo!(),
KeyCode::Backspace | KeyCode::Left => lv.back(),
KeyCode::Right => lv.enter(),
KeyCode::Right => lv.right(),
KeyCode::Enter => lv.enter(),
KeyCode::Char('u') => lv.undo(),
KeyCode::Char('r') => lv.redo(),
KeyCode::Char('f') => {
KeyCode::Tab => {
lv.input_state.target(InputTarget::Fields(None));
lv.footer_list.select(Some(0));
}
KeyCode::Esc => lv.input_state.reset(),
KeyCode::Char('s') if !lv.view.cursor.toplevel() => {
@ -515,20 +522,35 @@ impl App {
}
pub fn block_around(&self, area: Rect, buf: &mut Buffer, selected: bool) -> Rect {
let styles = self.styles();
let block = Block::bordered()
.style(styles.default)
.border_style(if selected {
styles.border_highlighted
} else {
styles.border
});
let inner = block.inner(area);
block.render(area, buf);
inner
block_around(area, buf, selected, &self.styles(), None::<&'static str>)
}
}
pub fn block_around(
area: Rect,
buf: &mut Buffer,
selected: bool,
styles: &Styles,
title: Option<impl Into<Cow<'static, str>>>,
) -> Rect {
let mut block = Block::bordered()
.style(styles.default)
.border_style(if selected {
styles.border_highlighted
} else {
styles.border
})
.merge_borders(MergeStrategy::Fuzzy);
if let Some(title) = title {
block = block.title_top(title.into());
}
let inner = block.inner(area);
block.render(area, buf);
inner
}
pub struct Styles {
default: Style,
highlighted: Style,
@ -580,7 +602,6 @@ impl Widget for &mut App {
};
let main_area = self.block_around(main_area, buf, header_focused);
let footer_area = self.block_around(footer_area, buf, footer_focused);
let [left, middle, right] = Layout::horizontal([
Constraint::Ratio(1, 3),
@ -669,39 +690,39 @@ impl Widget for &mut App {
&lv.filters,
selected_offset,
&lv.input_state,
lv.footer_list.selected.and_then(|idx| {
lv.footer_fields()
.get(idx)
.map(|(a, b)| (a.clone(), b.clone()))
}),
lv.get_selected_field(),
self.last_error.clone(),
)
.styled_ref(&styles)
.render(main_area, buf);
let items = lv.footer_fields();
lv.last_fields_offset = footer_area.y as usize;
lv.last_fields_height = items.len();
FieldTree::new(lv, footer_focused)
.styled_mut(&styles)
.render(footer_area, buf);
let width = 20;
let builder = ListBuilder::new(|cx| {
let Some((k, v)) = &items.get(cx.index) else {
return (Paragraph::new(""), 1);
};
let mut res =
Paragraph::new(format!("{k:width$} {v}")).wrap(Wrap { trim: false });
if cx.is_selected {
res = res.style(styles.highlighted);
}
let height = res.line_count(footer_area.width) as u16;
(res, height)
});
let list = ListView::new(builder, items.len()).style(styles.default);
StatefulWidget::render(list, footer_area, buf, &mut lv.footer_list);
// let items = lv.footer_fields();
// lv.last_fields_offset = footer_area.y as usize;
// lv.last_fields_height = items.len();
//
// let width = 20;
// let builder = ListBuilder::new(|cx| {
// let Some((k, v)) = &items.get(cx.index) else {
// return (Paragraph::new(""), 1);
// };
//
// let mut res =
// Paragraph::new(format!("{k:width$} {v}")).wrap(Wrap { trim: false });
//
// if cx.is_selected {
// res = res.style(styles.highlighted);
// }
//
// let height = res.line_count(footer_area.width) as u16;
// (res, height)
// });
//
// let list = ListView::new(builder, items.len()).style(styles.default);
// StatefulWidget::render(list, footer_area, buf, &mut lv.footer_list);
}
Tab::Empty => {}
Tab::Help => {