(frame: &mut Frame, app: &mut App, area: Rect)
| 379 | } |
| 380 | |
| 381 | fn draw_chat(frame: &mut Frame, app: &mut App, area: Rect) { |
| 382 | let layout = Layout::vertical([ |
| 383 | Constraint::Min(1), // messages |
| 384 | Constraint::Length(3), // input |
| 385 | ]) |
| 386 | .split(area); |
| 387 | |
| 388 | // Messages area |
| 389 | let msg_area = layout[0]; |
| 390 | let inner_width = msg_area.width.saturating_sub(2); |
| 391 | let visible_height = msg_area.height.saturating_sub(2); |
| 392 | |
| 393 | let all_lines = build_message_lines(app, inner_width); |
| 394 | app.total_lines = all_lines.len() as u16; |
| 395 | |
| 396 | if app.auto_scroll { |
| 397 | app.scroll_to_bottom(visible_height); |
| 398 | } |
| 399 | |
| 400 | let msg_block = Block::default() |
| 401 | .borders(Borders::ALL) |
| 402 | .border_style(Style::default().fg(Color::DarkGray)); |
| 403 | |
| 404 | let messages_widget = Paragraph::new(all_lines) |
| 405 | .block(msg_block) |
| 406 | .scroll((app.scroll_offset, 0)); |
| 407 | |
| 408 | frame.render_widget(messages_widget, msg_area); |
| 409 | |
| 410 | if app.total_lines > visible_height { |
| 411 | let mut scrollbar_state = ScrollbarState::new(app.total_lines as usize) |
| 412 | .position(app.scroll_offset as usize) |
| 413 | .viewport_content_length(visible_height as usize); |
| 414 | frame.render_stateful_widget( |
| 415 | Scrollbar::new(ScrollbarOrientation::VerticalRight), |
| 416 | msg_area.inner(Margin::new(0, 1)), |
| 417 | &mut scrollbar_state, |
| 418 | ); |
| 419 | } |
| 420 | |
| 421 | // Input area |
| 422 | let input_block = Block::default() |
| 423 | .borders(Borders::ALL) |
| 424 | .border_style(if app.streaming { |
| 425 | Style::default().fg(Color::DarkGray) |
| 426 | } else { |
| 427 | Style::default().fg(Color::Cyan) |
| 428 | }) |
| 429 | .title(" message "); |
| 430 | |
| 431 | let input_widget = Paragraph::new(app.input.as_str()).block(input_block); |
| 432 | frame.render_widget(input_widget, layout[1]); |
| 433 | |
| 434 | if !app.streaming { |
| 435 | frame.set_cursor_position(( |
| 436 | layout[1].x + 1 + app.cursor_pos as u16, |
| 437 | layout[1].y + 1, |
| 438 | )); |
no test coverage detected