1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! TUI main event loop.
//!
//! Thin entry point that creates a `TuiContext` and runs the event loop.
//! All handler logic lives in `tui_context.rs` and its sub-modules.
//!
//! Supporting modules:
//! - `tui_context` — shared state struct + handler methods
//! - `tui_types` — enums, type aliases, constants
//! - `tui_viewport` — viewport drawing and terminal lifecycle
//! - `tui_history` — command history persistence
//! - `tui_commands` — slash command dispatch
//! - `tui_render` — streaming inference output rendering
//! - `tui_output` — low-level terminal output helpers
//!
//! See #209 for the refactoring plan.
use crate::sink::UiEvent;
use crate::tui_context::TuiContext;
use anyhow::Result;
use koda_core::config::KodaConfig;
use koda_core::db::Database;
use koda_core::engine::EngineCommand;
use ratatui::{
style::{Color, Style},
text::Line,
};
use std::path::PathBuf;
use tokio::sync::mpsc;
/// Run the main interactive event loop with persistent TUI.
pub async fn run(
project_root: PathBuf,
config: KodaConfig,
db: Database,
session_id: String,
version_check: tokio::task::JoinHandle<Option<String>>,
first_run: bool,
) -> Result<()> {
let mut ctx = TuiContext::new(
project_root,
config,
db,
session_id,
version_check,
first_run,
)
.await?;
// First-run welcome message (after terminal is initialized)
if first_run {
ctx.emit(Line::styled(
" \u{1f43b} Welcome to Koda! Let's pick your LLM provider.",
Style::default().fg(Color::Cyan),
));
}
// Channels stay in run() — consumed by tokio::select! in different ways
let (ui_tx, mut ui_rx) = mpsc::unbounded_channel::<UiEvent>();
let (cmd_tx, mut cmd_rx) = mpsc::channel::<EngineCommand>(32);
// Initial viewport draw
ctx.draw()?;
// ── Main event loop ────────────────────────────────────────
ctx.run_event_loop(&ui_tx, &mut ui_rx, &cmd_tx, &mut cmd_rx)
.await?;
// ── Cleanup ────────────────────────────────────────────
ctx.cleanup().await;
Ok(())
}