use anyhow::Result;
use cursive::direction::Orientation;
use cursive::traits::*;
use cursive::views::{
Button, Dialog, DummyView, EditView, LinearLayout, ListView, NamedView, Panel, ResizedView,
ScrollView, SelectView, TextView,
};
use cursive::{Cursive, CursiveExt, View};
use cursive_multiplex::{Id, Mux};
use uuid::Uuid;
use super::super::state::AdvancedChatState;
use super::super::types::AgentState;
use super::handlers::show_context_menu;
use super::handlers::*;
pub struct AdvancedChatUI {
pub state: AdvancedChatState,
}
impl AdvancedChatUI {
pub fn new() -> Result<Self> {
let state = AdvancedChatState::new()?;
Ok(AdvancedChatUI { state })
}
pub fn setup_far_ui(&self, siv: &mut Cursive) {
let state = self.state.clone();
let mut main_layout = LinearLayout::horizontal();
let terminal_size = siv.screen_size();
let left_width = if terminal_size.x > 120 {
35 } else if terminal_size.x > 80 {
30 } else {
25 };
let chat_list_panel = self.create_chat_list_panel();
main_layout.add_child(ResizedView::with_fixed_width(left_width, chat_list_panel));
let chat_panel = self.create_chat_panel();
main_layout.add_child(chat_panel.full_width());
let title = if let Some(session) = self.state.get_active_session() {
match session.agent_state {
AgentState::Idle => "OSVM Agent - Idle".to_string(),
AgentState::Thinking => "OSVM Agent - Thinking...".to_string(),
AgentState::Planning => "OSVM Agent - Planning...".to_string(),
AgentState::ExecutingTool(ref tool) => format!("OSVM Agent - Executing {}", tool),
AgentState::Waiting => "OSVM Agent - Waiting".to_string(),
AgentState::Paused => "OSVM Agent - Paused".to_string(),
AgentState::Error(ref err) => format!("OSVM Agent - Error: {}", err),
}
} else {
"OSVM Agent".to_string()
};
let dialog = Dialog::around(main_layout)
.title(title)
.title_position(cursive::align::HAlign::Center);
siv.add_fullscreen_layer(dialog);
siv.focus_name("input").ok();
siv.add_global_callback(cursive::event::Key::Tab, |s| {
if s.focus_name("chat_list").is_ok() {
} else {
let _ = s.focus_name("input");
}
});
siv.add_global_callback(
cursive::event::Event::Shift(cursive::event::Key::Tab),
|s| {
if s.focus_name("input").is_ok() {
} else {
let _ = s.focus_name("chat_list");
}
},
);
siv.add_global_callback(cursive::event::Key::F10, |s| {
show_context_menu(s, cursive::Vec2::new(0, 0));
});
siv.add_global_callback(
cursive::event::Event::Shift(cursive::event::Key::F10),
|s| {
show_context_menu(s, cursive::Vec2::new(0, 0));
},
);
siv.add_global_callback(cursive::event::Event::WindowResize, |s| {
super::display::update_ui_displays(s);
});
}
pub fn setup_action_hotkeys(&self, siv: &mut Cursive) {
siv.add_global_callback(cursive::event::Event::AltChar('m'), |siv| {
siv.add_layer(
Dialog::text("Switching back to standard mode requires restarting.\nPlease restart with 'osvm chat' (without --advanced)")
.title("Switch Mode")
.button("OK", |s| { s.pop_layer(); })
);
});
let state = self.state.clone();
siv.add_global_callback(cursive::event::Event::AltChar('r'), move |s| {
retry_last_message(s, state.clone());
});
let state = self.state.clone();
siv.add_global_callback(cursive::event::Event::AltChar('c'), move |s| {
copy_last_message(s, state.clone());
});
let state = self.state.clone();
siv.add_global_callback(cursive::event::Event::AltChar('d'), move |s| {
delete_last_message(s, state.clone());
});
let state = self.state.clone();
siv.add_global_callback(cursive::event::Event::AltChar('f'), move |s| {
fork_conversation(s, state.clone());
});
}
pub fn setup_suggestion_hotkeys(&self, siv: &mut Cursive) {
for i in 1..=5 {
let state = self.state.clone();
let key_char = char::from_digit(i as u32, 10).unwrap();
siv.add_global_callback(cursive::event::Event::CtrlChar(key_char), move |s| {
let suggestions_visible = state
.suggestions_visible
.read()
.map(|v| *v)
.unwrap_or(false);
if suggestions_visible && s.find_name::<EditView>("input").is_some() {
insert_suggestion_at_cursor(s, (i - 1) as usize, state.clone());
}
});
}
for i in 1..=5 {
let state = self.state.clone();
siv.add_global_callback(
cursive::event::Event::AltChar(char::from_digit(i as u32, 10).unwrap()),
move |s| {
let suggestions_visible = state
.suggestions_visible
.read()
.map(|v| *v)
.unwrap_or(false);
if suggestions_visible && s.find_name::<EditView>("input").is_some() {
insert_suggestion_at_cursor(s, (i - 1) as usize, state.clone());
}
},
);
}
let state = self.state.clone();
siv.add_global_callback(cursive::event::Key::Esc, move |_s| {
if let Ok(mut vis) = state.suggestions_visible.write() {
*vis = false;
}
});
let state = self.state.clone();
siv.add_global_callback(cursive::event::Event::AltChar('x'), move |siv| {
if let Some(session) = state.get_active_session() {
let session_id = session.id;
if let Err(e) = state.remove_last_processing_message(session_id) {
eprintln!("Emergency clear failed: {}", e);
}
super::display::update_ui_displays(siv);
}
});
}
}