···1515 #[arg(short, long, value_name = "FLOAT", default_value_t = 60.0)]
1616 pub frame_rate: f64,
17171818+ /// Open the visualizer along with the tui
1919+ #[arg(short, long, default_value_t = false)]
2020+ pub visualizer: bool,
2121+1822 #[command(subcommand)]
1923 pub command: Option<Commands>,
2024}
+56
src/gui/mod.rs
···11+use std::{
22+ process,
33+ sync::{
44+ Arc,
55+ atomic::{AtomicBool, Ordering},
66+ },
77+};
88+99+use eframe::egui;
1010+1111+/// The `Filaments Visualizer`, which is an instance of `eframe`, which uses `egui`
1212+#[derive(Default)]
1313+pub struct FilViz {
1414+ shutdown_signal: Arc<AtomicBool>,
1515+ /// example for now
1616+ text: String,
1717+}
1818+1919+impl FilViz {
2020+ /// Create a new instance of the `FiLViz`
2121+ const fn new(_cc: &eframe::CreationContext<'_>, shutdown_signal: Arc<AtomicBool>) -> Self {
2222+ // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.
2323+ // Restore app state using cc.storage (requires the "persistence" feature).
2424+ // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
2525+ // for e.g. egui::PaintCallback.
2626+ Self {
2727+ shutdown_signal,
2828+ text: String::new(),
2929+ }
3030+ }
3131+3232+ /// Create and run the `FilViz`.
3333+ pub fn run(shutdown_signal: Arc<AtomicBool>) -> color_eyre::Result<()> {
3434+ let native_options = eframe::NativeOptions::default();
3535+ eframe::run_native(
3636+ "Filaments Visualizer",
3737+ native_options,
3838+ Box::new(|cc| Ok(Box::new(Self::new(cc, shutdown_signal)))),
3939+ )?;
4040+4141+ Ok(())
4242+ }
4343+}
4444+4545+impl eframe::App for FilViz {
4646+ fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
4747+ if self.shutdown_signal.load(Ordering::Relaxed) {
4848+ process::exit(0)
4949+ }
5050+5151+ egui::CentralPanel::default().show_inside(ui, |ui| {
5252+ ui.heading("Hello World!");
5353+ ui.text_edit_singleline(&mut self.text);
5454+ });
5555+ }
5656+}
+40-10
src/main.rs
···22//! My (suri.codes) personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
33//!
4455-use crate::{cli::Cli, tui::TuiApp};
55+use std::sync::{
66+ Arc,
77+ atomic::{AtomicBool, Ordering},
88+};
99+1010+use crate::{cli::Cli, gui::FilViz, tui::TuiApp};
611use clap::Parser;
712813mod cli;
1414+mod gui;
1515+mod tui;
1616+917mod config;
1018mod errors;
1119mod logging;
1212-mod tui;
13201414-#[tokio::main]
1515-async fn main() -> color_eyre::Result<()> {
2121+fn main() -> color_eyre::Result<()> {
1622 errors::init()?;
1723 logging::init()?;
18241925 let args = Cli::parse();
20262121- // if there is any subcommand, we want to execute that, otherwise we
2222- // just run the app
2727+ // create a new tokio runtime, shared behind arc
2828+ let rt = Arc::new(tokio::runtime::Runtime::new()?);
2929+3030+ // if there are any commands, run those and exit
2331 if let Some(command) = args.command {
2424- return command.process();
3232+ return rt.block_on(async { command.process() });
2533 }
26342727- // if no command we run the app
2828- let mut tui = TuiApp::new(args.tick_rate, args.frame_rate)?;
2929- tui.run().await?;
3535+ let shutdown_signal = Arc::new(AtomicBool::new(false));
30363737+ // then we spawn the tui on its own thread
3838+ let tui_handle = std::thread::spawn({
3939+ let tui_rt = rt.clone();
4040+ let shutdown = shutdown_signal.clone();
4141+ move || -> color_eyre::Result<()> {
4242+ // block the tui on the same runtime as above
4343+ tui_rt.block_on(async {
4444+ let mut tui = TuiApp::new(args.tick_rate, args.frame_rate)?;
4545+ tui.run().await?;
4646+ shutdown.store(true, Ordering::Relaxed);
4747+ Ok(())
4848+ })
4949+ }
5050+ });
5151+5252+ // if they asked for the visualizer, we give them the visualizer
5353+ if args.visualizer {
5454+ // enter the guard so egui_async works properly
5555+ let _rt_guard = rt.enter();
5656+ FilViz::run(shutdown_signal)?;
5757+ }
5858+5959+ // join on the tui
6060+ tui_handle.join().unwrap()?;
3161 Ok(())
3262}