(&mut self, frame: &mut Frame, area: Rect, chat_state: &ChatState)
| 114 | } |
| 115 | |
| 116 | fn render_messages(&mut self, frame: &mut Frame, area: Rect, chat_state: &ChatState) { |
| 117 | let title = if self.browse_mode { |
| 118 | format!(" Conversation [Browse Mode \u{2195}] ") |
| 119 | } else { |
| 120 | " Conversation ".to_string() |
| 121 | }; |
| 122 | |
| 123 | let block = Block::default() |
| 124 | .borders(Borders::ALL) |
| 125 | .border_style(self.theme.style(StyleKind::Border)) |
| 126 | .style(Style::default().bg(self.theme.background)) |
| 127 | .title(title); |
| 128 | |
| 129 | let inner = block.inner(area); |
| 130 | frame.render_widget(block, area); |
| 131 | |
| 132 | // Store messages area for mouse click hit-testing |
| 133 | self.messages_area = Some(inner); |
| 134 | // Regions are recalculated each frame for the currently rendered (visible subset) list. |
| 135 | self.block_tool_regions.clear(); |
| 136 | self.thinking_regions.clear(); |
| 137 | let available_width = inner.width; |
| 138 | |
| 139 | if chat_state.messages.is_empty() { |
| 140 | let welcome = vec![ |
| 141 | Line::from(""), |
| 142 | Line::from(Span::styled( |
| 143 | "Welcome to BitFun CLI!", |
| 144 | self.theme.style(StyleKind::Title), |
| 145 | )), |
| 146 | Line::from(""), |
| 147 | Line::from(Span::styled( |
| 148 | "Enter your request, AI will help you complete programming tasks.", |
| 149 | self.theme.style(StyleKind::Info), |
| 150 | )), |
| 151 | Line::from(""), |
| 152 | Line::from(Span::styled( |
| 153 | "Tip: Use / prefix for quick commands", |
| 154 | self.theme.style(StyleKind::Muted), |
| 155 | )), |
| 156 | ]; |
| 157 | |
| 158 | let paragraph = Paragraph::new(welcome) |
| 159 | .alignment(Alignment::Center) |
| 160 | .style(Style::default().bg(self.theme.background)) |
| 161 | .wrap(Wrap { trim: true }); |
| 162 | |
| 163 | frame.render_widget(paragraph, inner); |
| 164 | self.visible_plain_lines.clear(); |
| 165 | self.selection_anchor = None; |
| 166 | self.selection_focus = None; |
| 167 | self.selection_mouse_down = None; |
| 168 | self.selection_dragged = false; |
| 169 | } else { |
| 170 | let visible_lines = inner.height as usize; |
| 171 | |
| 172 | // ── Step 1: Ensure all messages are in the render cache and collect line counts ── |
| 173 | let mut msg_line_counts: Vec<usize> = Vec::with_capacity(chat_state.messages.len()); |
no test coverage detected