vtcode_tui/core_tui/session/
impl_render.rs1use super::*;
2
3impl Session {
4 pub fn render(&mut self, frame: &mut Frame<'_>) {
5 let viewport = frame.area();
6 if viewport.height == 0 || viewport.width == 0 {
7 return;
8 }
9
10 if self.needs_full_clear {
12 frame.render_widget(Clear, viewport);
13 self.needs_full_clear = false;
14 }
15
16 let header_lines = self.header_lines();
18 let header_height = self.header_height_from_lines(viewport.width, &header_lines);
19 if header_height != self.header_rows {
20 self.header_rows = header_height;
21 self.recalculate_transcript_rows();
22 }
23
24 let mode = self.resolved_layout_mode(viewport);
25 let status_height = if viewport.width > 0 && !mode.show_footer() {
28 1
29 } else {
30 0
31 };
32 let inner_width = viewport.width.saturating_sub(2);
33 let desired_lines = self.desired_input_lines(inner_width);
34 let block_height = Self::input_block_height_for_lines(desired_lines);
35 let input_height = block_height.saturating_add(status_height);
36 self.apply_input_height(input_height);
37
38 let mut constraints = vec![Constraint::Length(header_height), Constraint::Min(1)];
39 constraints.push(Constraint::Length(input_height));
40
41 let segments = Layout::vertical(constraints).split(viewport);
42
43 let header_area = segments[0];
44 let main_area = segments[1];
45 let input_index = segments.len().saturating_sub(1);
46 let input_area = segments[input_index];
47
48 let _available_width = main_area.width;
49 let _horizontal_minimum = ui::INLINE_CONTENT_MIN_WIDTH + ui::INLINE_NAVIGATION_MIN_WIDTH;
50
51 let (transcript_area, modal_area) = render::split_inline_modal_area(self, main_area);
52 let (transcript_area, file_palette_area) =
53 render::split_inline_file_palette_area(self, transcript_area);
54 let (transcript_area, history_picker_area) =
55 render::split_inline_history_picker_area(self, transcript_area);
56 let (transcript_area, slash_area) = slash::split_inline_slash_area(self, transcript_area);
57 let navigation_area = Rect::new(main_area.x, main_area.y, 0, 0); SessionWidget::new(self)
61 .header_lines(header_lines.clone())
62 .header_area(header_area)
63 .transcript_area(transcript_area)
64 .navigation_area(navigation_area) .render(viewport, frame.buffer_mut());
66
67 self.render_input(frame, input_area);
71 if let Some(modal_area) = modal_area {
72 render::render_modal(self, frame, modal_area);
73 } else {
74 render::render_modal(self, frame, viewport);
75 }
76 if let Some(file_palette_area) = file_palette_area {
77 render::render_file_palette(self, frame, file_palette_area);
78 }
79 if let Some(history_picker_area) = history_picker_area {
80 render::render_history_picker(self, frame, history_picker_area);
81 }
82 if let Some(slash_area) = slash_area {
83 slash::render_slash_palette(self, frame, slash_area);
84 }
85
86 if self.diff_preview.is_some() {
88 diff_preview::render_diff_preview(self, frame, viewport);
89 }
90
91 if self.mouse_selection.has_selection || self.mouse_selection.is_selecting {
93 self.mouse_selection
94 .apply_highlight(frame.buffer_mut(), viewport);
95
96 if self.mouse_selection.needs_copy() {
98 let text = self
99 .mouse_selection
100 .extract_text(frame.buffer_mut(), viewport);
101 if !text.is_empty() {
102 MouseSelectionState::copy_to_clipboard(&text);
103 }
104 self.mouse_selection.mark_copied();
105 }
106 }
107 }
108
109 #[allow(dead_code)]
110 pub(crate) fn render_message_spans(&self, index: usize) -> Vec<Span<'static>> {
111 let Some(line) = self.lines.get(index) else {
112 return vec![Span::raw(String::new())];
113 };
114 message_renderer::render_message_spans(
115 line,
116 &self.theme,
117 &self.labels,
118 |kind| self.prefix_text(kind),
119 |line| self.prefix_style(line),
120 |kind| self.text_fallback(kind),
121 )
122 }
123}