mod app;
mod bookmarks;
#[cfg(feature = "demo")]
mod demo;
mod lineage;
mod nodes;
#[cfg_attr(feature = "demo", allow(dead_code))]
mod runtime;
mod streams;
mod tabs;
mod theme;
mod widgets;
use app::App;
#[tokio::main(flavor = "multi_thread")]
async fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let prev_panic_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
ratatui::restore();
prev_panic_hook(info);
}));
let nrpc_tail = streams::NrpcTail::new(streams::NRPC_TAIL_CAP);
#[cfg(feature = "demo")]
let harness = demo::spawn(nrpc_tail.clone()).await?;
#[cfg(not(feature = "demo"))]
let harness = runtime::spawn().await?;
let deck = harness.deck();
let blob_adapters = harness.blob_adapters();
let logs_tail = streams::LogsTail::new(streams::LOGS_TAIL_CAP);
let _logs_stream_task = streams::spawn_logs_stream(deck.clone(), logs_tail.clone());
let audit_tail = streams::AuditTail::new(streams::AUDIT_TAIL_CAP);
let _audit_stream_task = streams::spawn_audit_stream(deck.clone(), audit_tail.clone());
let failures_tail = streams::FailuresTail::new(streams::FAILURES_TAIL_CAP);
let _failures_stream_task = streams::spawn_failures_stream(deck.clone(), failures_tail.clone());
let blobs_tail = streams::BlobsTail::new();
let bookmarks = bookmarks::BookmarkStore::load().unwrap_or_else(|err| {
eprintln!("[deck] bookmark store: {err} — using empty store");
bookmarks::BookmarkStore::empty()
});
let this_node = harness.this_node();
let terminal = ratatui::init();
let tails = streams::Tails {
logs: logs_tail,
audit: audit_tail,
failures: failures_tail,
blobs: blobs_tail.clone(),
nrpc: nrpc_tail.clone(),
};
let app = App::new(deck, tails, blob_adapters.clone(), bookmarks, this_node);
let _blobs_poll_task = if blob_adapters.is_empty() {
None
} else {
Some(streams::spawn_blobs_poll(
blob_adapters,
blobs_tail,
std::time::Duration::from_millis(500),
app.toast_tx.clone(),
))
};
drop(nrpc_tail);
let pending_admin = app.pending_admin_handle();
let result = app.run(terminal);
ratatui::restore();
let handles: Vec<_> = std::mem::take(&mut *pending_admin.lock());
for h in handles {
let _ = tokio::time::timeout(std::time::Duration::from_secs(2), h).await;
}
#[cfg(feature = "demo")]
let _ = harness.into_shutdown().await;
#[cfg(not(feature = "demo"))]
drop(harness);
result
}