mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-31 16:49:34 +00:00
Fix TuiChannel integration and enable in main.rs
- Store event_tx in Arc<Mutex<>> so respond() can send to TUI - Fix run_event_loop to take owned receiver - Switch main.rs from SimpleCliChannel to TuiChannel - Add proper terminal cleanup on TUI error Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
09032d69cb
commit
4ae59ef52c
@@ -20,7 +20,7 @@ pub fn run_event_loop(
|
||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||
app: &mut AppState,
|
||||
msg_tx: mpsc::Sender<IncomingMessage>,
|
||||
event_rx: &mut mpsc::Receiver<AppEvent>,
|
||||
mut event_rx: mpsc::Receiver<AppEvent>,
|
||||
) -> io::Result<()> {
|
||||
loop {
|
||||
// Render
|
||||
@@ -31,7 +31,7 @@ pub fn run_event_loop(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Poll for events
|
||||
// Poll for terminal events
|
||||
if event::poll(TICK_RATE)? {
|
||||
let evt = event::read()?;
|
||||
if let Err(e) = handle_event(app, evt, &msg_tx) {
|
||||
@@ -39,7 +39,7 @@ pub fn run_event_loop(
|
||||
}
|
||||
}
|
||||
|
||||
// Check for app events (non-blocking)
|
||||
// Check for app events from agent (non-blocking)
|
||||
while let Ok(app_event) = event_rx.try_recv() {
|
||||
handle_app_event(app, app_event);
|
||||
}
|
||||
|
||||
+29
-20
@@ -23,7 +23,7 @@ use crossterm::{
|
||||
};
|
||||
use ratatui::Terminal;
|
||||
use ratatui::backend::CrosstermBackend;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
|
||||
use crate::channels::{Channel, IncomingMessage, MessageStream, OutgoingResponse};
|
||||
@@ -36,13 +36,15 @@ pub use overlay::{ApprovalOverlay, ApprovalRequest};
|
||||
/// TUI channel for interactive terminal input with Ratatui.
|
||||
pub struct TuiChannel {
|
||||
/// Channel for sending events to the TUI.
|
||||
event_tx: Option<mpsc::Sender<AppEvent>>,
|
||||
event_tx: Arc<Mutex<Option<mpsc::Sender<AppEvent>>>>,
|
||||
}
|
||||
|
||||
impl TuiChannel {
|
||||
/// Create a new TUI channel.
|
||||
pub fn new() -> Self {
|
||||
Self { event_tx: None }
|
||||
Self {
|
||||
event_tx: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,21 +64,21 @@ impl Channel for TuiChannel {
|
||||
let (msg_tx, msg_rx) = mpsc::channel(32);
|
||||
let (event_tx, event_rx) = mpsc::channel(64);
|
||||
|
||||
// Store the event sender so we can send responses
|
||||
// Note: In the actual implementation, we'd store this properly
|
||||
// For now, spawn the TUI in a separate task
|
||||
let event_tx_clone = event_tx.clone();
|
||||
// Store the event sender for respond()
|
||||
{
|
||||
let mut guard = self.event_tx.lock().await;
|
||||
*guard = Some(event_tx);
|
||||
}
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
if let Err(e) = run_tui(msg_tx, event_rx) {
|
||||
// Try to restore terminal even on error
|
||||
let _ = disable_raw_mode();
|
||||
let _ = execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture);
|
||||
tracing::error!("TUI error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Keep the event_tx alive by storing it
|
||||
// This is a hack; in production we'd use Arc<Mutex<>> or similar
|
||||
let _ = event_tx_clone;
|
||||
|
||||
Ok(Box::pin(ReceiverStream::new(msg_rx)))
|
||||
}
|
||||
|
||||
@@ -85,25 +87,32 @@ impl Channel for TuiChannel {
|
||||
_msg: &IncomingMessage,
|
||||
response: OutgoingResponse,
|
||||
) -> Result<(), ChannelError> {
|
||||
// Send response event to the TUI
|
||||
if let Some(ref tx) = self.event_tx {
|
||||
let _ = tx
|
||||
.send(AppEvent::Response(response.content))
|
||||
let guard = self.event_tx.lock().await;
|
||||
if let Some(ref tx) = *guard {
|
||||
tx.send(AppEvent::Response(response.content))
|
||||
.await
|
||||
.map_err(|e| ChannelError::SendFailed {
|
||||
name: "tui".to_string(),
|
||||
reason: e.to_string(),
|
||||
});
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn health_check(&self) -> Result<(), ChannelError> {
|
||||
Ok(())
|
||||
let guard = self.event_tx.lock().await;
|
||||
if guard.is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ChannelError::HealthCheckFailed {
|
||||
name: "tui".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn shutdown(&self) -> Result<(), ChannelError> {
|
||||
if let Some(ref tx) = self.event_tx {
|
||||
let guard = self.event_tx.lock().await;
|
||||
if let Some(ref tx) = *guard {
|
||||
let _ = tx.send(AppEvent::Quit).await;
|
||||
}
|
||||
Ok(())
|
||||
@@ -113,7 +122,7 @@ impl Channel for TuiChannel {
|
||||
/// Run the TUI event loop (blocking).
|
||||
fn run_tui(
|
||||
msg_tx: mpsc::Sender<IncomingMessage>,
|
||||
mut event_rx: mpsc::Receiver<AppEvent>,
|
||||
event_rx: mpsc::Receiver<AppEvent>,
|
||||
) -> io::Result<()> {
|
||||
// Setup terminal
|
||||
enable_raw_mode()?;
|
||||
@@ -126,7 +135,7 @@ fn run_tui(
|
||||
let mut app = AppState::new();
|
||||
|
||||
// Run event loop
|
||||
let result = events::run_event_loop(&mut terminal, &mut app, msg_tx, &mut event_rx);
|
||||
let result = events::run_event_loop(&mut terminal, &mut app, msg_tx, event_rx);
|
||||
|
||||
// Restore terminal
|
||||
disable_raw_mode()?;
|
||||
|
||||
Reference in New Issue
Block a user