Implement chat and markdown views, enhance status bar, and add workflow panel
- Added `chat.rs` for rendering chat messages with timestamps and roles. - Introduced `markdown.rs` for rendering markdown content with styling. - Created `status.rs` to display the application status bar with session and message counts. - Developed `workflow.rs` to show the current workflow status, including tool calls and active jobs. - Established a `theme.rs` for centralized color management across the UI. - Updated `mod.rs` to include new modules and manage rendering logic.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use super::job::BashJob;
|
||||
|
||||
pub(crate) fn bash_jobs_map() -> &'static Mutex<HashMap<String, BashJob>> {
|
||||
static JOBS: OnceLock<Mutex<HashMap<String, BashJob>>> = OnceLock::new();
|
||||
JOBS.get_or_init(|| Mutex::new(HashMap::new()))
|
||||
}
|
||||
|
||||
pub fn bash_output(id: &str) -> Option<Vec<String>> {
|
||||
let mut map = bash_jobs_map().lock().ok()?;
|
||||
let job = map.get_mut(id)?;
|
||||
let mut lines = Vec::new();
|
||||
while let Some(line) = job.try_read_line() {
|
||||
lines.push(line);
|
||||
}
|
||||
if lines.is_empty() { None } else { Some(lines) }
|
||||
}
|
||||
|
||||
pub fn bash_kill(id: &str) -> anyhow::Result<()> {
|
||||
let mut map = bash_jobs_map().lock().map_err(|e| anyhow::anyhow!("lock error: {}", e))?;
|
||||
let job = map.remove(id);
|
||||
if job.is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
anyhow::bail!("bash job '{}' not found", id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bash_job(job: BashJob) -> String {
|
||||
let id = job.id.clone();
|
||||
if let Ok(mut map) = bash_jobs_map().lock() {
|
||||
map.insert(id.clone(), job);
|
||||
}
|
||||
id
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::io::BufRead;
|
||||
|
||||
pub struct BashJob {
|
||||
pub id: String,
|
||||
pub command: String,
|
||||
pub started_at: i64,
|
||||
pub output_rx: mpsc::Receiver<String>,
|
||||
pub exit_code: Option<i32>,
|
||||
pub handle: Option<thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
pub fn spawn_bash_job(command: String) -> BashJob {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let started_at = chrono::Utc::now().timestamp_millis();
|
||||
let (output_tx, output_rx) = mpsc::channel::<String>();
|
||||
let cmd = command.clone();
|
||||
|
||||
let handle = thread::spawn(move || {
|
||||
let child = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&cmd)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn();
|
||||
match child {
|
||||
Ok(mut child) => {
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
let reader = std::io::BufReader::new(stdout);
|
||||
for line in reader.lines().map_while(Result::ok) {
|
||||
let _ = output_tx.send(line);
|
||||
}
|
||||
}
|
||||
let status = child.wait();
|
||||
let code = status.ok().and_then(|s| s.code());
|
||||
let _ = output_tx.send(format!("__exit:{}", code.unwrap_or(-1)));
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = output_tx.send(format!("__error:{}", e));
|
||||
let _ = output_tx.send("__exit:-1".to_string());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BashJob {
|
||||
id,
|
||||
command,
|
||||
started_at,
|
||||
output_rx,
|
||||
exit_code: None,
|
||||
handle: Some(handle),
|
||||
}
|
||||
}
|
||||
|
||||
impl BashJob {
|
||||
pub fn try_read_line(&mut self) -> Option<String> {
|
||||
match self.output_rx.try_recv() {
|
||||
Ok(line) => {
|
||||
if line.starts_with("__exit:") {
|
||||
self.exit_code = line.strip_prefix("__exit:").and_then(|s| s.parse().ok());
|
||||
None
|
||||
} else {
|
||||
Some(line)
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.exit_code.is_none()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod control;
|
||||
pub mod job;
|
||||
Reference in New Issue
Block a user