Files
guru-connect/agent/src/chat/mod.rs
Mike Swanson d0de888dd1
All checks were successful
Build and Test / Build Agent (Windows) (push) Successful in 6m53s
Build and Test / Build Server (Linux) (push) Successful in 10m59s
Build and Test / Security Audit (push) Successful in 4m31s
Build and Test / Build Summary (push) Successful in 10s
style(agent): clear 77 pre-existing clippy -D warnings
CI never ran clippy on the agent crate (the build-server clippy job is
Linux-only and can't compile the Windows agent; build-agent only runs cargo
build), so 77 clippy -D-warnings errors had accumulated. Behavior-preserving
cleanup, code-reviewed APPROVED, locally verified (cargo clippy --workspace
--all-targets --all-features -- -D warnings exits 0; cargo test --workspace =
57 passed).

- let _ = on Win32 resource-teardown BOOL returns (gdi.rs); fallible
  BitBlt/GetDIBits stay error-handled
- removed unused imports/vars; idiom fixes (div_ceil, is_null, transmute
  annotations, match collapsing, useless_conversion)
- #[allow(dead_code)] + comment on genuine Task-6/7 scaffolding (vk consts,
  SpecialKey emission, SAS mgmt API, modifier tracking, GDI frame-diff fields)
- Cargo.lock: cargo pruned ~147 stale transitive entries (no version changes)

Follow-up: add cargo clippy -D warnings to the build-agent CI job so the agent
crate stays clippy-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 08:51:45 -07:00

173 lines
4.7 KiB
Rust

//! 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<ChatCommand>,
message_rx: Arc<Mutex<Receiver<ChatMessage>>>,
_handle: thread::JoinHandle<()>,
}
impl ChatController {
/// Create a new chat controller (spawns chat window thread)
#[cfg(windows)]
pub fn new() -> Option<Self> {
let (command_tx, command_rx) = mpsc::channel::<ChatCommand>();
let (message_tx, message_rx) = mpsc::channel::<ChatMessage>();
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<Self> {
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<ChatMessage> {
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<ChatCommand>, _message_tx: Sender<ChatMessage>) {
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<u16> = OsStr::new(title)
.encode_wide()
.chain(std::iter::once(0))
.collect();
let message_wide: Vec<u16> = 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<ChatCommand>, _message_tx: Sender<ChatMessage>) {
// No-op on non-Windows
}