Implement next-gen modules: chrs-code-edit (Git), chrs-discovery (LibP2P), and chrs-observer (TUI)
This commit is contained in:
14
chrs-observer/Cargo.toml
Normal file
14
chrs-observer/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "chrs-observer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ratatui = "0.26"
|
||||
crossterm = "0.27"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
chrs-mail = { path = "../chrs-mail" }
|
||||
chrs-bubble = { path = "../chrs-bubble" }
|
||||
chrs-backbeat = { path = "../chrs-backbeat" }
|
||||
chrono = "0.4"
|
||||
serde_json = "1.0"
|
||||
103
chrs-observer/src/main.rs
Normal file
103
chrs-observer/src/main.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
//! chrs-observer: Real-time TUI dashboard for CHORUS.
|
||||
|
||||
use ratatui::{
|
||||
backend::CrosstermBackend,
|
||||
widgets::{Block, Borders, Paragraph, List, ListItem},
|
||||
layout::{Layout, Constraint, Direction},
|
||||
Terminal,
|
||||
};
|
||||
use crossterm::{
|
||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use std::{error::Error, io, time::{Duration, Instant}};
|
||||
|
||||
struct App {
|
||||
pulse_bpm: u32,
|
||||
beat_index: u32,
|
||||
logs: Vec<String>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new() -> App {
|
||||
App {
|
||||
pulse_bpm: 30,
|
||||
beat_index: 0,
|
||||
logs: vec!["[OBSERVER] Initialized.".into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Setup terminal
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
// Create app and run loop
|
||||
let app = App::new();
|
||||
let res = run_app(&mut terminal, app);
|
||||
|
||||
// Restore terminal
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
if let Err(err) = res {
|
||||
println!("{:?}", err)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_app<B: ratatui::backend::Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
|
||||
let tick_rate = Duration::from_millis(250);
|
||||
let mut last_tick = Instant::now();
|
||||
loop {
|
||||
terminal.draw(|f| ui(f, &app))?;
|
||||
|
||||
let timeout = tick_rate
|
||||
.checked_sub(last_tick.elapsed())
|
||||
.unwrap_or_else(|| Duration::from_secs(0));
|
||||
if crossterm::event::poll(timeout)? {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if let KeyCode::Char('q') = key.code {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
if last_tick.elapsed() >= tick_rate {
|
||||
// In a real app, we would update beat_index from chrs-backbeat here
|
||||
last_tick = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ui(f: &mut ratatui::Frame, app: &App) {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.margin(1)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(0),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(f.size());
|
||||
|
||||
let header = Paragraph::new(format!("CHORUS CLUSTER DASHBOARD | BPM: {} | BEAT: {}", app.pulse_bpm, app.beat_index))
|
||||
.block(Block::default().borders(Borders::ALL).title("Pulse"));
|
||||
f.render_widget(header, chunks[0]);
|
||||
|
||||
let logs: Vec<ListItem> = app.logs.iter().rev().map(|s| ListItem::new(s.as_str())).collect();
|
||||
let log_list = List::new(logs).block(Block::default().borders(Borders::ALL).title("Live Events"));
|
||||
f.render_widget(log_list, chunks[1]);
|
||||
}
|
||||
Reference in New Issue
Block a user