Skip to main content

codetether_agent/tui/app/event_loop/
mod.rs

1//! Main TUI event loop orchestration.
2//!
3//! Drives the terminal draw → event → dispatch cycle using
4//! `tokio::select!` across terminal, session, result, watchdog
5//! and tick channels.  Each iteration redraws the UI, then
6//! waits for whichever source fires first.
7//!
8//! # Examples
9//!
10//! ```ignore
11//! run_event_loop(
12//!     &mut terminal, &mut app, cwd, registry,
13//!     &mut session, &mut bus, bridge, tx, rx, rtx, rrx,
14//! ).await?;
15//! ```
16
17mod autochat;
18mod select_loop;
19mod shutdown;
20mod smart_retry;
21mod terminal;
22mod watchdog;
23mod watchdog_spawn;
24
25use std::{path::Path, sync::Arc, time::Duration};
26
27use crossterm::event::EventStream;
28use ratatui::{Terminal, backend::CrosstermBackend};
29use tokio::sync::mpsc;
30
31use crate::bus::BusHandle;
32use crate::provider::ProviderRegistry;
33use crate::session::{Session, SessionEvent};
34use crate::tui::app::{state::App, worker_bridge::sync_worker_bridge_agents};
35use crate::tui::{
36    constants::MAIN_PROCESSING_WATCHDOG_TIMEOUT_SECS, ui::main::ui, worker_bridge::TuiWorkerBridge,
37};
38
39/// Drive the TUI draw-event-dispatch loop until quit.
40///
41/// Continuously redraws the UI, then uses `tokio::select!`
42/// to multiplex terminal events, session events, results,
43/// a watchdog stall timer, and a 50 ms background tick.
44///
45/// # Examples
46///
47/// ```ignore
48/// run_event_loop(
49///     &mut terminal, &mut app, cwd, registry,
50///     &mut session, &mut bus, bridge, tx, rx, rtx, rrx,
51/// ).await?;
52/// ```
53pub async fn run_event_loop(
54    terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>,
55    app: &mut App,
56    cwd: &Path,
57    registry: Option<Arc<ProviderRegistry>>,
58    session: &mut Session,
59    bus_handle: &mut BusHandle,
60    mut worker_bridge: Option<TuiWorkerBridge>,
61    event_tx: mpsc::Sender<SessionEvent>,
62    mut event_rx: mpsc::Receiver<SessionEvent>,
63    result_tx: mpsc::Sender<anyhow::Result<Session>>,
64    mut result_rx: mpsc::Receiver<anyhow::Result<Session>>,
65) -> anyhow::Result<()> {
66    let mut reader = EventStream::new();
67    let mut shutdown_rx = crate::tui::app::signal_shutdown::spawn_shutdown_listener();
68    let tick = Duration::from_millis(50);
69    let mut tick_timer = tokio::time::interval(tick);
70    let wd_interval = Duration::from_secs(MAIN_PROCESSING_WATCHDOG_TIMEOUT_SECS);
71    let mut wd_timer = tokio::time::interval(wd_interval);
72
73    loop {
74        sync_worker_bridge_agents(app, &worker_bridge);
75        terminal.draw(|f| ui(f, app, session))?;
76        if select_loop::select_once(
77            &mut reader,
78            app,
79            cwd,
80            session,
81            &registry,
82            &mut worker_bridge,
83            &event_tx,
84            &result_tx,
85            &mut event_rx,
86            &mut result_rx,
87            &mut shutdown_rx,
88            &mut wd_timer,
89            wd_interval,
90            &mut tick_timer,
91            bus_handle,
92        )
93        .await?
94        {
95            break;
96        }
97    }
98
99    shutdown::deregister_bridge(&worker_bridge);
100    Ok(())
101}