kernelx_core/interfaces/tui/
app.rs

1use super::{model::AppModel, terminal::TerminalManager, types::ChatEvent, update, view};
2use crate::{capabilities::Chat, models::Model, providers::Provider, Result};
3use anyhow::Context;
4use crossterm::event::{self, Event, KeyCode};
5use std::time::Duration;
6use tokio::sync::mpsc;
7
8pub struct App<P: Provider + Chat + Clone + 'static> {
9    terminal: TerminalManager,
10    model: AppModel<P>,
11    tx: mpsc::Sender<ChatEvent>,
12    rx: mpsc::Receiver<ChatEvent>,
13}
14
15impl<P: Provider + Chat + Clone + 'static> App<P> {
16    pub fn new(model: Model<P, dyn Chat>) -> Result<Self> {
17        let model = AppModel::new(model);
18        let (tx, rx) = mpsc::channel(32);
19        Ok(Self {
20            terminal: TerminalManager::new()?,
21            model,
22            tx,
23            rx,
24        })
25    }
26
27    pub async fn run(&mut self) -> Result<()> {
28        let tx = self.tx.clone();
29
30        tokio::spawn(async move {
31            loop {
32                if let Ok(true) = event::poll(Duration::from_millis(100)) {
33                    if let Ok(Event::Key(key)) = event::read() {
34                        let _ = tx.send(ChatEvent::Input(key.code)).await;
35                    }
36                }
37            }
38        });
39
40        while !self.model.should_quit {
41            self.terminal
42                .terminal
43                .draw(|f| view::draw(f, &self.model))
44                .context("Failed to draw terminal")?;
45
46            if let Some(event) = self.rx.recv().await {
47                if let ChatEvent::Input(KeyCode::Enter) = event {
48                    if !self.model.input.is_empty() {
49                        let input = std::mem::take(&mut self.model.input);
50                        self.model.push_message(super::Message::User(input.clone()));
51                        let processing_idx = self.model.push_message(super::Message::Processing);
52
53                        let tx = self.tx.clone();
54                        let chat_state = self.model.chat_state.clone();
55
56                        tokio::spawn(async move {
57                            let mut chat_state = chat_state.lock().await;
58                            match (*chat_state).send_message(input).await {
59                                Ok(response) => {
60                                    let _ = tx
61                                        .send(ChatEvent::Response {
62                                            message: response,
63                                            index: processing_idx,
64                                        })
65                                        .await;
66                                }
67                                Err(e) => {
68                                    let _ = tx
69                                        .send(ChatEvent::Error {
70                                            error: e.to_string(),
71                                            index: processing_idx,
72                                        })
73                                        .await;
74                                }
75                            }
76                        });
77                    }
78                } else {
79                    update::update(&mut self.model, event);
80                }
81            }
82        }
83
84        Ok(())
85    }
86}