Add chat functionality between technician and client

- Add ChatMessage to protobuf definitions
- Server relays chat messages between agent and viewer
- Agent chat module shows messages via MessageBox
- Dashboard chat modal with WebSocket connection
- Simplified protobuf encoder/decoder in JavaScript

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-28 16:31:16 -07:00
parent 0dcbae69a0
commit aa03a87c7c
6 changed files with 694 additions and 9 deletions

View File

@@ -7,10 +7,11 @@
//! - Input event handling
use crate::capture::{self, Capturer, Display};
use crate::chat::{ChatController, ChatMessage as ChatMsg};
use crate::config::Config;
use crate::encoder::{self, Encoder};
use crate::input::InputController;
use crate::proto::{Message, message};
use crate::proto::{Message, message, ChatMessage};
use crate::transport::WebSocketTransport;
use crate::tray::{TrayController, TrayAction};
use anyhow::Result;
@@ -148,8 +149,8 @@ impl SessionManager {
Ok(())
}
/// Run the session main loop with tray event processing
pub async fn run_with_tray(&mut self, tray: Option<&TrayController>) -> Result<()> {
/// Run the session main loop with tray and chat event processing
pub async fn run_with_tray(&mut self, tray: Option<&TrayController>, chat: Option<&ChatController>) -> Result<()> {
if self.transport.is_none() {
anyhow::bail!("Not connected");
}
@@ -215,9 +216,38 @@ impl SessionManager {
};
for msg in messages {
// Handle chat messages specially
if let Some(message::Payload::ChatMessage(chat_msg)) = &msg.payload {
if let Some(c) = chat {
c.add_message(ChatMsg {
id: chat_msg.id.clone(),
sender: chat_msg.sender.clone(),
content: chat_msg.content.clone(),
timestamp: chat_msg.timestamp,
});
}
continue; // Don't pass to handle_message
}
self.handle_message(&mut input, msg)?;
}
// Check for outgoing chat messages from user
if let Some(c) = chat {
if let Some(outgoing) = c.poll_outgoing() {
let chat_proto = ChatMessage {
id: outgoing.id,
sender: "client".to_string(),
content: outgoing.content,
timestamp: outgoing.timestamp,
};
let msg = Message {
payload: Some(message::Payload::ChatMessage(chat_proto)),
};
let transport = self.transport.as_mut().unwrap();
transport.send(msg).await?;
}
}
// Capture and send frame if interval elapsed
if last_frame_time.elapsed() >= frame_interval {
last_frame_time = Instant::now();