//! Chat window for the agent //! //! Provides a simple chat interface for communication between //! the technician and the end user. use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::thread; use tracing::info; #[cfg(not(windows))] use tracing::warn; #[cfg(windows)] use windows::core::PCWSTR; #[cfg(windows)] use windows::Win32::UI::WindowsAndMessaging::*; /// A chat message #[derive(Debug, Clone)] pub struct ChatMessage { pub id: String, pub sender: String, pub content: String, pub timestamp: i64, } /// Commands that can be sent to the chat window // Show/Hide/Close are part of the chat control API but not yet driven by the session loop. #[derive(Debug)] pub enum ChatCommand { #[allow(dead_code)] Show, #[allow(dead_code)] Hide, AddMessage(ChatMessage), #[allow(dead_code)] Close, } /// Controller for the chat window pub struct ChatController { command_tx: Sender, message_rx: Arc>>, _handle: thread::JoinHandle<()>, } impl ChatController { /// Create a new chat controller (spawns chat window thread) #[cfg(windows)] pub fn new() -> Option { let (command_tx, command_rx) = mpsc::channel::(); let (message_tx, message_rx) = mpsc::channel::(); let handle = thread::spawn(move || { run_chat_window(command_rx, message_tx); }); Some(Self { command_tx, message_rx: Arc::new(Mutex::new(message_rx)), _handle: handle, }) } #[cfg(not(windows))] pub fn new() -> Option { warn!("Chat window not supported on this platform"); None } /// Show the chat window #[allow(dead_code)] pub fn show(&self) { let _ = self.command_tx.send(ChatCommand::Show); } /// Hide the chat window #[allow(dead_code)] pub fn hide(&self) { let _ = self.command_tx.send(ChatCommand::Hide); } /// Add a message to the chat window pub fn add_message(&self, msg: ChatMessage) { let _ = self.command_tx.send(ChatCommand::AddMessage(msg)); } /// Check for outgoing messages from the user pub fn poll_outgoing(&self) -> Option { if let Ok(rx) = self.message_rx.lock() { rx.try_recv().ok() } else { None } } /// Close the chat window #[allow(dead_code)] pub fn close(&self) { let _ = self.command_tx.send(ChatCommand::Close); } } #[cfg(windows)] fn run_chat_window(command_rx: Receiver, _message_tx: Sender) { info!("Starting chat window thread"); // For now, we'll use a simple message box approach // A full implementation would create a proper window with a text input // Process commands loop { match command_rx.recv() { Ok(ChatCommand::Show) => { info!("Chat window: Show requested"); // Show a simple notification that chat is available } Ok(ChatCommand::Hide) => { info!("Chat window: Hide requested"); } Ok(ChatCommand::AddMessage(msg)) => { info!("Chat message received: {} - {}", msg.sender, msg.content); // Show the message to the user via a message box (simple implementation) let title = format!("Message from {}", msg.sender); let content = msg.content.clone(); // Spawn a thread to show the message box (non-blocking) thread::spawn(move || { show_message_box_internal(&title, &content); }); } Ok(ChatCommand::Close) => { info!("Chat window: Close requested"); break; } Err(_) => { // Channel closed break; } } } } #[cfg(windows)] fn show_message_box_internal(title: &str, message: &str) { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; let title_wide: Vec = OsStr::new(title) .encode_wide() .chain(std::iter::once(0)) .collect(); let message_wide: Vec = OsStr::new(message) .encode_wide() .chain(std::iter::once(0)) .collect(); unsafe { MessageBoxW( None, PCWSTR(message_wide.as_ptr()), PCWSTR(title_wide.as_ptr()), MB_OK | MB_ICONINFORMATION | MB_TOPMOST | MB_SETFOREGROUND, ); } } #[cfg(not(windows))] fn run_chat_window(_command_rx: Receiver, _message_tx: Sender) { // No-op on non-Windows }