My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: workspace + better closing behavior for visualizer!

+113 -46
+6 -6
flake.lock
··· 8 8 "rust-analyzer-src": "rust-analyzer-src" 9 9 }, 10 10 "locked": { 11 - "lastModified": 1774555695, 12 - "narHash": "sha256-JpTx7Rn8sILuXAH9a0K0UCZST5KY9ZTMzrZ61KcsNno=", 11 + "lastModified": 1774682177, 12 + "narHash": "sha256-OVbuJnJLlbHE28eRMudjtA6NXz/ifuXSho79gvh6GHY=", 13 13 "owner": "nix-community", 14 14 "repo": "fenix", 15 - "rev": "d76ca95395662ed18b02b894e487eb41fd0e7d99", 15 + "rev": "e0f515387df77b9fdbaaf81e7f866f0365474c18", 16 16 "type": "github" 17 17 }, 18 18 "original": { ··· 44 44 "rust-analyzer-src": { 45 45 "flake": false, 46 46 "locked": { 47 - "lastModified": 1774454876, 48 - "narHash": "sha256-bwkM8HseUs/22x+hy6FWvJMP6q/2CKBrm4sYxz9rMY8=", 47 + "lastModified": 1774569884, 48 + "narHash": "sha256-E8iWEPzg7OnE0XXXjo75CX7xFauqzJuGZ5wSO9KS8Ek=", 49 49 "owner": "rust-lang", 50 50 "repo": "rust-analyzer", 51 - "rev": "9253d39eab8b9c9da3c1412fc94764e01d55a02b", 51 + "rev": "443ddcddd0c73b07b799d052f5ef3b448c2f3508", 52 52 "type": "github" 53 53 }, 54 54 "original": {
+3 -9
src/cli/process.rs
··· 9 9 use crate::{ 10 10 cli::Commands, 11 11 config::{Config, get_config_dir}, 12 + types::Workspace, 12 13 }; 13 14 14 15 impl Commands { 15 - pub fn process(self) -> Result<()> { 16 + pub async fn process(self) -> Result<()> { 16 17 match self { 17 18 Self::Init { name } => { 18 19 // create the directory ··· 20 21 .context("Failed to get current directory")? 21 22 .join(&name); 22 23 23 - // create the .filaments folder 24 - let filaments_dir = dir.join(".filaments"); 25 - 26 - create_dir_all(&filaments_dir) 27 - .context("Failed to create the filaments directory!")?; 28 - 29 - // create the database inside there 30 - File::create(filaments_dir.join("filaments.db"))?; 24 + Workspace::initialize(dir.clone()).await?; 31 25 32 26 // write config that sets the filaments directory to current dir! 33 27 let config_kdl = dbg! {Config::generate(&dir)};
+3 -17
src/gui/mod.rs
··· 1 - use std::{ 2 - process, 3 - sync::{ 4 - Arc, 5 - atomic::{AtomicBool, Ordering}, 6 - }, 7 - }; 8 - 9 1 use eframe::egui; 10 2 11 3 /// The `Filaments Visualizer`, which is an instance of `eframe`, which uses `egui` 12 4 #[derive(Default)] 13 5 pub struct FilViz { 14 - shutdown_signal: Arc<AtomicBool>, 15 6 /// example for now 16 7 text: String, 17 8 } 18 9 19 10 impl FilViz { 20 11 /// Create a new instance of the `FiLViz` 21 - const fn new(_cc: &eframe::CreationContext<'_>, shutdown_signal: Arc<AtomicBool>) -> Self { 12 + const fn new(_cc: &eframe::CreationContext<'_>) -> Self { 22 13 // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style. 23 14 // Restore app state using cc.storage (requires the "persistence" feature). 24 15 // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use 25 16 // for e.g. egui::PaintCallback. 26 17 Self { 27 - shutdown_signal, 28 18 text: String::new(), 29 19 } 30 20 } 31 21 32 22 /// Create and run the `FilViz`. 33 - pub fn run(shutdown_signal: Arc<AtomicBool>) -> color_eyre::Result<()> { 23 + pub fn run() -> color_eyre::Result<()> { 34 24 let native_options = eframe::NativeOptions::default(); 35 25 eframe::run_native( 36 26 "Filaments Visualizer", 37 27 native_options, 38 - Box::new(|cc| Ok(Box::new(Self::new(cc, shutdown_signal)))), 28 + Box::new(|cc| Ok(Box::new(Self::new(cc)))), 39 29 )?; 40 30 41 31 Ok(()) ··· 44 34 45 35 impl eframe::App for FilViz { 46 36 fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { 47 - if self.shutdown_signal.load(Ordering::Relaxed) { 48 - process::exit(0) 49 - } 50 - 51 37 egui::CentralPanel::default().show_inside(ui, |ui| { 52 38 ui.heading("Hello World!"); 53 39 ui.text_edit_singleline(&mut self.text);
+5 -11
src/main.rs
··· 2 2 //! My (suri.codes) personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities. 3 3 //! 4 4 5 - use std::sync::{ 6 - Arc, 7 - atomic::{AtomicBool, Ordering}, 8 - }; 5 + use std::{process, sync::Arc}; 9 6 10 7 use crate::{cli::Cli, gui::FilViz, tui::TuiApp}; 11 8 use clap::Parser; ··· 30 27 31 28 // if there are any commands, run those and exit 32 29 if let Some(command) = args.command { 33 - return rt.block_on(async { command.process() }); 30 + return rt.block_on(async { command.process().await }); 34 31 } 35 32 36 - let shutdown_signal = Arc::new(AtomicBool::new(false)); 37 - 38 33 // then we spawn the tui on its own thread 39 34 let tui_handle = std::thread::spawn({ 40 35 // arc stuff 41 36 let tui_rt = rt.clone(); 42 - let shutdown = shutdown_signal.clone(); 43 37 44 38 // closure to run the tui 45 39 move || -> color_eyre::Result<()> { ··· 47 41 tui_rt.block_on(async { 48 42 let mut tui = TuiApp::new(args.tick_rate, args.frame_rate)?; 49 43 tui.run().await?; 50 - shutdown.store(true, Ordering::Relaxed); 51 - Ok(()) 44 + // just close everything as soon as the tui is done running 45 + process::exit(0); 52 46 }) 53 47 } 54 48 }); ··· 57 51 if args.visualizer { 58 52 // enter the guard so egui_async works properly 59 53 let _rt_guard = rt.enter(); 60 - FilViz::run(shutdown_signal)?; 54 + FilViz::run()?; 61 55 } 62 56 63 57 // join on the tui
-1
src/types/mod.rs
··· 18 18 pub use task::Task; 19 19 20 20 mod workspace; 21 - #[expect(unused_imports)] 22 21 pub use workspace::Workspace;
+96 -2
src/types/workspace.rs
··· 1 - use dto::DatabaseConnection; 1 + use std::path::PathBuf; 2 + 3 + use color_eyre::eyre::{Context, Result}; 4 + use dto::{Database, DatabaseConnection}; 5 + use tokio::fs::{File, create_dir_all}; 6 + use tracing::debug; 2 7 8 + /// The `Workspace` in which the filaments exist. 9 + #[expect(dead_code)] 10 + #[derive(Debug, Clone)] 3 11 pub struct Workspace { 4 - db: DatabaseConnection, 12 + /// Private field so it can only be instantiated from a `Path` 13 + _private: (), 14 + /// Connection to the sqlite database inside the `Workspace` 15 + pub db: DatabaseConnection, 16 + /// The path to the root of this workspace 17 + pub root: PathBuf, 18 + } 19 + 20 + impl Workspace { 21 + /// Given a path, try to construct a `Workspace` based on its contents. 22 + /// 23 + /// Note: this means that there should already exist a valid `Workspace` 24 + /// at that path. 25 + pub async fn instansiate(path: impl Into<PathBuf>) -> Result<Self> { 26 + let path = path.into(); 27 + 28 + let db_conn_string = format!( 29 + "sqlite://{}", 30 + path.clone() 31 + .join(".filaments/filaments.db") 32 + .canonicalize() 33 + .context("Invalid Filaments workspace!!")? 34 + .to_string_lossy() 35 + ); 36 + 37 + debug!("connecting to {db_conn_string}"); 38 + 39 + let conn = Database::connect(db_conn_string) 40 + .await 41 + .context("Failed to connect to the database in the filaments workspace!")?; 42 + 43 + Ok(Self { 44 + _private: (), 45 + db: conn, 46 + root: path, 47 + }) 48 + } 49 + 50 + pub async fn initialize(path: impl Into<PathBuf>) -> Result<Self> { 51 + let path = path.into(); 52 + 53 + // create the .filaments folder 54 + let filaments_dir = path.join(".filaments"); 55 + 56 + create_dir_all(&filaments_dir) 57 + .await 58 + .context("Failed to create the filaments directory!")?; 59 + 60 + // create the database inside there 61 + File::create(filaments_dir.join("filaments.db")).await?; 62 + 63 + Ok(Self::instansiate(path).await.expect( 64 + "Invariant broken. This instantiation call must always work 65 + since we just initialized the workspace.", 66 + )) 67 + } 68 + } 69 + 70 + #[cfg(test)] 71 + mod tests { 72 + use std::{ 73 + fs::{File, create_dir_all}, 74 + path::PathBuf, 75 + }; 76 + 77 + use dto::NanoId; 78 + 79 + use crate::types::Workspace; 80 + 81 + #[tokio::test] 82 + async fn test_instantiation() { 83 + let path = PathBuf::from("/tmp/filaments/.filaments/filaments.db"); 84 + create_dir_all(path.parent().unwrap()).unwrap(); 85 + let _ = File::create(&path).unwrap(); 86 + let _ws = Workspace::instansiate(dbg!(&path.parent().unwrap().parent().unwrap())) 87 + .await 88 + .unwrap(); 89 + } 90 + 91 + #[tokio::test] 92 + async fn test_initialization() { 93 + let path = PathBuf::from(format!("/tmp/filaments/{}", NanoId::default())); 94 + 95 + Workspace::initialize(path) 96 + .await 97 + .expect("Should initialize just fine"); 98 + } 5 99 }