use super::spinner::Spinner;
use ratatui::widgets::{ListState, ScrollbarState};
#[derive(Default, Debug)]
pub struct State {
pub current_response: String,
pub horizontal_scroll_state: ScrollbarState,
pub horizontal_scroll: usize,
pub input: String,
pub input_mode: InputMode,
pub input_width: u16,
pub list_state: ListState,
pub messages: Vec<(String, String)>,
pub show_toggle: bool,
pub spinner: Spinner,
pub vertical_scroll_state: ScrollbarState,
pub quit: bool,
}
impl State {
#[must_use]
pub fn new() -> Self {
let mut list_state = ListState::default();
list_state.select(Some(0));
State {
current_response: String::new(),
horizontal_scroll_state: ScrollbarState::default(),
horizontal_scroll: 0,
input: String::new(),
input_mode: InputMode::Normal,
input_width: 0,
list_state,
messages: Vec::new(),
show_toggle: false,
spinner: Spinner::new(),
vertical_scroll_state: ScrollbarState::default(),
quit: false,
}
}
pub fn scroll_up(&mut self) {
let current = self.list_state.selected().unwrap_or(0);
let next = current.saturating_sub(1);
self.list_state.select(Some(next));
self.vertical_scroll_state = self.vertical_scroll_state.position(next);
}
pub fn scroll_down(&mut self) {
let current = self.list_state.selected().unwrap_or(0);
let next = (current + 1).min(self.messages.len().saturating_sub(1));
self.list_state.select(Some(next));
self.vertical_scroll_state = self.vertical_scroll_state.position(next);
}
pub fn update_response(&mut self, new_content: &str) {
if self.input_mode == InputMode::Waiting {
self.current_response.push_str(new_content);
if let Some((role, content)) = self.messages.last_mut() {
if role == "assistant" {
content.clone_from(&self.current_response);
} else {
self.messages
.push(("assistant".to_string(), self.current_response.clone()));
}
} else {
self.messages
.push(("assistant".to_string(), self.current_response.clone()));
}
let is_at_bottom = self.list_state.selected() == Some(self.messages.len() - 1);
if is_at_bottom {
self.list_state.select(Some(self.messages.len() - 1));
self.vertical_scroll_state =
self.vertical_scroll_state.position(self.messages.len() - 1);
}
}
}
pub fn add_response(&mut self, response: String) {
if self.input_mode == InputMode::Waiting {
self.messages.pop();
if let Some((role, content)) = self.messages.last() {
if role == "system" && content == "Generating..." {
self.messages.pop();
}
}
}
self.messages.push(("assistant".to_string(), response));
self.input_mode = InputMode::Normal;
self.current_response.clear();
self.list_state.select(Some(self.messages.len() - 1));
self.vertical_scroll_state = self.vertical_scroll_state.position(self.messages.len() - 1);
self.horizontal_scroll = 0;
self.horizontal_scroll_state = ScrollbarState::default();
}
pub fn start_new_response(&mut self) {
self.input_mode = InputMode::Waiting;
self.current_response.clear();
self.messages.push(("assistant".to_string(), String::new()));
}
}
#[derive(Default, Debug, PartialEq, Clone, Copy)]
pub enum InputMode {
#[default]
Normal,
Editing,
Waiting,
}
#[derive(PartialEq)]
pub enum Action {
CancelRequest,
}