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