use super::{ChatMode, ChatView, MessageRole};
use crate::tui::state::{ChatOverlayMessage, ChatOverlayMessageRole, ChatOverlayState};
impl ChatView {
pub fn toggle_mode(&mut self) {
self.chat_mode = match self.chat_mode {
ChatMode::Infer => ChatMode::Agent,
ChatMode::Agent => ChatMode::Infer,
};
}
pub fn toggle_deep_thinking(&mut self) {
self.deep_thinking = !self.deep_thinking;
}
pub fn set_chat_mode(&mut self, mode: ChatMode) {
self.chat_mode = mode;
}
pub fn set_provider(&mut self, name: impl Into<String>) {
self.provider_name = name.into();
}
}
impl ChatView {
pub fn get_chat_state(&self) -> ChatOverlayState {
let messages = self
.messages
.iter()
.map(|msg| {
let role = match msg.role {
MessageRole::User => ChatOverlayMessageRole::User,
MessageRole::Nika => ChatOverlayMessageRole::Nika,
MessageRole::System => ChatOverlayMessageRole::System,
MessageRole::Tool => ChatOverlayMessageRole::Tool,
};
ChatOverlayMessage::new(role, &msg.content)
})
.collect();
ChatOverlayState {
messages,
input: self.input.value().to_string(),
cursor: self.input.cursor(),
scroll: self.scroll,
history: self.history.clone(),
history_index: self.history_index,
is_streaming: self.is_streaming,
partial_response: self.partial_response.clone(),
current_model: self.current_model.clone(),
edit_history: Default::default(), }
}
}
impl ChatView {
pub fn tick_flash(&mut self) {
if self.copy_flash_index.is_some() {
let elapsed = self.frame.wrapping_sub(self.copy_flash_start);
if elapsed > 16 {
self.copy_flash_index = None;
}
}
}
pub fn update_scroll_totals(&mut self) {
self.conversation_scroll.set_total(self.messages.len());
self.activity_scroll.set_total(self.activity_items.len());
}
}
#[allow(dead_code)]
pub(super) fn char_to_byte_offset(s: &str, char_offset: usize) -> usize {
s.char_indices()
.nth(char_offset)
.map(|(i, _)| i)
.unwrap_or(s.len())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_char_to_byte_offset_ascii() {
let s = "hello world";
assert_eq!(char_to_byte_offset(s, 0), 0);
assert_eq!(char_to_byte_offset(s, 5), 5);
assert_eq!(char_to_byte_offset(s, 11), 11);
assert_eq!(char_to_byte_offset(s, 100), 11); }
#[test]
fn test_char_to_byte_offset_unicode() {
let s = "héllo 🦋 wörld";
assert_eq!(char_to_byte_offset(s, 0), 0); assert_eq!(char_to_byte_offset(s, 1), 1); assert_eq!(char_to_byte_offset(s, 2), 3); assert_eq!(char_to_byte_offset(s, 6), 7); assert_eq!(char_to_byte_offset(s, 7), 11); }
#[test]
fn test_centered_rect() {
use crate::tui::widgets::centered_rect;
use ratatui::layout::Rect;
let area = Rect::new(0, 0, 100, 50);
let centered = centered_rect(60, 80, area);
assert!(centered.x > 0);
assert!(centered.y > 0);
assert!(centered.width < area.width);
assert!(centered.height < area.height);
}
#[test]
fn test_toggle_mode() {
let mut view = ChatView::new();
assert!(matches!(view.chat_mode, ChatMode::Infer));
view.toggle_mode();
assert!(matches!(view.chat_mode, ChatMode::Agent));
view.toggle_mode();
assert!(matches!(view.chat_mode, ChatMode::Infer));
}
#[test]
fn test_toggle_deep_thinking() {
let mut view = ChatView::new();
assert!(!view.deep_thinking);
view.toggle_deep_thinking();
assert!(view.deep_thinking);
view.toggle_deep_thinking();
assert!(!view.deep_thinking);
}
#[test]
fn test_set_chat_mode() {
let mut view = ChatView::new();
view.set_chat_mode(ChatMode::Agent);
assert!(matches!(view.chat_mode, ChatMode::Agent));
view.set_chat_mode(ChatMode::Infer);
assert!(matches!(view.chat_mode, ChatMode::Infer));
}
#[test]
fn test_set_provider() {
let mut view = ChatView::new();
view.set_provider("OpenAI GPT-4");
assert_eq!(view.provider_name, "OpenAI GPT-4");
}
}