vtcode_ui/tui/core_tui/widgets/
header.rs1use ratatui::{
2 buffer::Buffer,
3 layout::Rect,
4 style::{Modifier, Style},
5 text::Line,
6 widgets::{Clear, Widget},
7};
8
9use crate::tui::ui::tui::session::Session;
10
11pub struct HeaderWidget<'a> {
30 session: &'a mut Session,
31 lines: std::sync::Arc<Vec<Line<'static>>>,
32 custom_style: Option<Style>,
33}
34
35impl<'a> HeaderWidget<'a> {
36 pub(crate) fn new(session: &'a mut Session) -> Self {
38 Self {
39 session,
40 lines: std::sync::Arc::new(Vec::new()),
41 custom_style: None,
42 }
43 }
44
45 #[must_use]
47 pub(crate) fn lines(mut self, lines: std::sync::Arc<Vec<Line<'static>>>) -> Self {
48 self.lines = lines;
49 self
50 }
51
52 #[must_use]
54 pub fn custom_style(mut self, style: Style) -> Self {
55 self.custom_style = Some(style);
56 self
57 }
58}
59
60impl<'a> Widget for HeaderWidget<'a> {
61 #[cfg_attr(feature = "profiling", hotpath::measure)]
62 fn render(self, area: Rect, buf: &mut Buffer) {
63 Clear.render(area, buf);
64
65 if area.height == 0 || area.width == 0 {
66 return;
67 }
68
69 let hide_header = self.session.appearance.hide_header;
72 let text_style = self.session.header_primary_style().add_modifier(Modifier::DIM);
73
74 if hide_header {
75 buf.set_style(area, text_style);
77 for (y, line) in (area.y..).zip(self.lines.iter().take(usize::from(area.height))) {
78 let mut x = area.x;
79 for span in &line.spans {
80 if x >= area.right() {
81 break;
82 }
83 let merged = text_style.patch(span.style);
84 let (end_x, _) =
85 buf.set_stringn(x, y, span.content.as_ref(), area.right().saturating_sub(x) as usize, merged);
86 x = end_x;
87 }
88 }
89 return;
90 }
91
92 let mut border_style = Style::default();
93 if let Some(accent) = self
94 .session
95 .theme
96 .tool_accent
97 .or(self.session.theme.primary)
98 .or(self.session.theme.foreground)
99 {
100 border_style = border_style.fg(crate::tui::core_tui::style::ratatui_color_from_ansi(accent));
101 }
102 let title = self.session.header_block_title_cached();
103 let block = ratatui::widgets::Block::bordered()
104 .title(title)
105 .border_type(crate::tui::core_tui::session::terminal_capabilities::get_border_type())
106 .border_style(border_style)
107 .style(self.session.styles.default_style());
108 let inner = block.inner(area);
109 block.render(area, buf);
110
111 if inner.width == 0 || inner.height == 0 {
112 return;
113 }
114 for (y, line) in (inner.y..).zip(self.lines.iter().take(usize::from(inner.height))) {
115 let mut x = inner.x;
116 for span in &line.spans {
117 if x >= inner.right() {
118 break;
119 }
120 let merged = text_style.patch(span.style);
122 let (end_x, _) =
123 buf.set_stringn(x, y, span.content.as_ref(), inner.right().saturating_sub(x) as usize, merged);
124 x = end_x;
125 }
126 }
127 }
128}