(app: &App, width: u16)
| 186 | } |
| 187 | |
| 188 | fn build_message_lines(app: &App, width: u16) -> Vec<Line<'static>> { |
| 189 | let mut lines: Vec<Line<'static>> = Vec::new(); |
| 190 | let wrap_width = if width > 4 { |
| 191 | width as usize - 4 |
| 192 | } else { |
| 193 | width as usize |
| 194 | }; |
| 195 | |
| 196 | for (i, msg) in app.messages.iter().enumerate() { |
| 197 | if i > 0 { |
| 198 | lines.push(Line::from("")); |
| 199 | } |
| 200 | |
| 201 | if msg.role == "system" { |
| 202 | continue; |
| 203 | } |
| 204 | |
| 205 | let is_last = i == app.messages.len() - 1; |
| 206 | |
| 207 | if msg.role == "user" { |
| 208 | let prefix = "you> "; |
| 209 | let style = Style::default() |
| 210 | .fg(Color::Cyan) |
| 211 | .add_modifier(Modifier::BOLD); |
| 212 | wrap_text(&mut lines, &msg.content, prefix, style, wrap_width); |
| 213 | } else { |
| 214 | let visible = strip_think_tags(&msg.content); |
| 215 | |
| 216 | if is_last && app.waiting { |
| 217 | // Before first token: show inline indicator |
| 218 | lines.push(Line::from(Span::styled( |
| 219 | " generating...", |
| 220 | Style::default() |
| 221 | .fg(Color::DarkGray) |
| 222 | .add_modifier(Modifier::ITALIC), |
| 223 | ))); |
| 224 | } else if is_last && app.thinking { |
| 225 | // Inside <think> block: show thinking label + content in gray |
| 226 | let think_content = extract_open_think(&msg.content); |
| 227 | lines.push(Line::from(Span::styled( |
| 228 | " thinking...", |
| 229 | Style::default() |
| 230 | .fg(Color::Yellow) |
| 231 | .add_modifier(Modifier::ITALIC), |
| 232 | ))); |
| 233 | if !think_content.is_empty() { |
| 234 | let style = Style::default().fg(Color::DarkGray); |
| 235 | wrap_text(&mut lines, &think_content, " ", style, wrap_width); |
| 236 | } |
| 237 | } else if !visible.is_empty() { |
| 238 | let style = Style::default().fg(Color::White); |
| 239 | wrap_text(&mut lines, &visible, " ", style, wrap_width); |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | lines |
| 245 | } |
no test coverage detected