diff --git a/src/main.rs b/src/main.rs
index 9582535..5be33d0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,9 +7,9 @@ use std::{process, sync::Arc};
use crate::{
cli::Cli,
config::Config,
- gui::FilViz,
tui::TuiApp,
- types::{Kasten, KastenHandle},
+ types::{Deimos, Kasten, KastenHandle},
+ viz::FilViz,
};
use clap::Parser;
use tokio::sync::{RwLock, mpsc};
@@ -18,11 +18,11 @@ use tracing::debug;
mod cli;
mod config;
mod errors;
-mod gui;
mod logging;
mod lsp;
mod tui;
mod types;
+mod viz;
fn main() -> color_eyre::Result<()> {
errors::init()?;
@@ -55,6 +55,7 @@ fn main() -> color_eyre::Result<()> {
// arc stuff
let tui_rt = rt.clone();
let kh = kh.clone();
+ let signal_tx = signal_tx.clone();
// closure to run the tui
move || -> color_eyre::Result<()> {
@@ -68,20 +69,20 @@ fn main() -> color_eyre::Result<()> {
}
});
- // spawn deimos
- // {
-
- // rt.spawn(async {
- // let deimos = Deimos::new(kh, fh);
- // deimos.watch().await
- // });
- // }
-
// if they asked for the visualizer, we give them the visualizer
if args.visualizer {
// enter the guard so egui_async works properly
let _rt_guard = rt.enter();
+ // spawn deimos
+ {
+ let kh = kh.clone();
+ rt.spawn(async {
+ let deimos = Deimos::new(kh, signal_tx);
+ deimos.watch().await
+ });
+ }
+
let index = rt.block_on(async { kh.read().await.index.clone() });
FilViz::run(kh, signal_rx, &index)?;
diff --git a/src/tui/signal.rs b/src/tui/signal.rs
index 0747245..d31a5a5 100644
--- a/src/tui/signal.rs
+++ b/src/tui/signal.rs
@@ -5,7 +5,10 @@ use strum::Display;
use serde::{Deserialize, Serialize};
-use crate::{tui::Region, types::ZettelId};
+use crate::{
+ tui::Region,
+ types::{Link, ZettelId},
+};
/// The varying signals that can be emitted.
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
@@ -31,10 +34,17 @@ pub enum Signal {
/// Create a New `Zettel`
NewZettel,
+ /// This zettel was created (filaments specific)
CreatedZettel {
zid: ZettelId,
},
+ /// Set the links for this `ZettelId` (filaments specific)
+ SetLinks {
+ zid: ZettelId,
+ links: Vec,
+ },
+
/// User asks to open a `Zettel`
OpenZettel,
/// The user is done editing a `Zettel`
diff --git a/src/types/deimos.rs b/src/types/deimos.rs
index 80381b9..173f43c 100644
--- a/src/types/deimos.rs
+++ b/src/types/deimos.rs
@@ -1,51 +1,66 @@
-// use color_eyre::eyre::Context;
-// use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Result, Watcher as _};
-// use tracing::{error, info};
+use color_eyre::eyre::Context;
+use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Result, Watcher as _};
+use tracing::{error, info};
-use crate::types::KastenHandle;
+use tokio::sync::mpsc::UnboundedSender;
+
+use crate::{
+ tui::Signal,
+ types::{KastenHandle, ZettelId},
+};
#[derive(Debug)]
-#[expect(dead_code)]
pub struct Deimos {
kh: KastenHandle,
- // fh: FilamentsHandle,
+ signal_tx: UnboundedSender,
}
-// impl Deimos {
-// pub const fn new(kh: KastenHandle, fh: FilamentsHandle) -> Self {
-// Self { kh, fh }
-// }
-
-// /// Watches the `Filaments Directory` for file changes and updates
-// /// the internal state of the kasten and the `Filaments` displayed in the
-// /// gui.
-// ///
-// /// NOTE: This function must be spawned as a top level await
-// pub async fn watch(&self) -> color_eyre::Result<()> {
-// info!("deimos spawned!");
-
-// let (tx, mut rx) = tokio::sync::mpsc::channel::>(10);
-// let mut watcher = RecommendedWatcher::new(
-// move |res| tx.blocking_send(res).expect("failed to send event"),
-// Config::default(),
-// )?;
-
-// watcher
-// .watch(&self.kh.read().await.root, RecursiveMode::Recursive)
-// .with_context(|| "failed to start the FS watcher")?;
-
-// while let Some(res) = rx.recv().await {
-// let kt = &mut *self.kh.write().await;
-// let fh = &mut *self.fh.lock().expect("Lock must not be poisoned");
-
-// match res {
-// Ok(event) => info!("event: {event:?}"),
-// Err(e) => error!("watch error: {e:?}"),
-// }
-
-// *fh = (&kt.index).into();
-// }
-
-// Ok(())
-// }
-// }
+impl Deimos {
+ pub const fn new(kh: KastenHandle, signal_tx: UnboundedSender) -> Self {
+ Self { kh, signal_tx }
+ }
+
+ /// Watches the `Filaments Directory` for file changes and updates
+ /// the internal state of the kasten and the `Filaments` displayed in the
+ /// gui.
+ ///
+ /// NOTE: This function must be spawned as a top level await
+ pub async fn watch(&self) -> color_eyre::Result<()> {
+ info!("deimos spawned!");
+
+ let (tx, mut rx) = tokio::sync::mpsc::channel::>(10);
+ let mut watcher = RecommendedWatcher::new(
+ move |res| tx.blocking_send(res).expect("failed to send event"),
+ Config::default(),
+ )?;
+
+ watcher
+ .watch(&self.kh.read().await.root, RecursiveMode::Recursive)
+ .with_context(|| "failed to start the FS watcher")?;
+
+ while let Some(res) = rx.recv().await {
+ let Ok(event) = res.inspect_err(|e| error!("watcher error: {e:?}")) else {
+ continue;
+ };
+
+ if let EventKind::Modify(notify::event::ModifyKind::Data(_)) = event.kind {
+ for path in event.paths {
+ let Ok(zid) = ZettelId::try_from(path.clone())
+ .inspect_err(|e| error!("Failed to convert path into zettel id! : {e}"))
+ else {
+ continue;
+ };
+
+ let kt = &mut *self.kh.write().await;
+ kt.process_path(path).await?;
+
+ let links = kt.index.get_links(&zid).clone();
+
+ self.signal_tx.send(Signal::SetLinks { zid, links })?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/types/filaments.rs b/src/types/filaments.rs
index 8668786..9e82c8b 100644
--- a/src/types/filaments.rs
+++ b/src/types/filaments.rs
@@ -23,6 +23,14 @@ pub struct Filaments {
}
impl Filaments {
+ pub fn get_gid(&self, zid: &ZettelId) -> NodeIndex {
+ *self
+ .zid_to_gid
+ .get(zid)
+ .expect("Invariant broken, any zid we ask for must have a corresponding gid")
+ }
+
+ /// Inserts a `Zettel` into the graph.
pub fn insert_zettel(&mut self, zid: ZettelId, index: &Index) {
let zod = index.get_zod(&zid);
@@ -36,7 +44,7 @@ impl Filaments {
fn custom_node_closure(zod: &ZettelOnDisk, node: &mut Node) {
node.set_label(zod.fm.title.clone());
let disp = node.display_mut();
- disp.radius = 50.0;
+ disp.radius = 75.0;
// randomize position
let x = rand::random_range(0.0..=100.0);
@@ -44,6 +52,25 @@ impl Filaments {
node.set_location(emath::Pos2 { x, y });
node.set_hovered(true);
}
+
+ /// Sets the `Links` for the given `ZettelId`
+ pub fn set_links_for_zid(&mut self, zid: &ZettelId, links: Vec) {
+ let gid = self.get_gid(zid);
+ self.graph
+ .g()
+ .edges(gid)
+ .map(|e| e.weight().id())
+ .collect::>()
+ .iter()
+ .for_each(|edge_index| {
+ self.graph.remove_edge(*edge_index);
+ });
+
+ for link in links {
+ let dest = self.get_gid(&link.dest);
+ self.graph.add_edge(gid, dest, link);
+ }
+ }
}
impl From<&Index> for Filaments {
diff --git a/src/types/index.rs b/src/types/index.rs
index 350d1f9..5f6cef3 100644
--- a/src/types/index.rs
+++ b/src/types/index.rs
@@ -53,7 +53,7 @@ impl Index {
let zid: ZettelId = path.as_path().try_into()?;
let (fm, body) = FrontMatter::extract_from_file(&path)?;
- let outgoing_links = Self::parse_outgoing_links(&zid, &body);
+ let outgoing_links = Self::_parse_outgoing_links(&zid, &body);
Ok((
zid,
@@ -94,7 +94,13 @@ impl Index {
})
}
- pub fn parse_outgoing_links(zid: &ZettelId, body: &str) -> Vec {
+ pub fn parse_outgoing_links(&mut self, zid: &ZettelId) {
+ let body = self.get_zod(zid).body.as_str();
+ let links = Self::_parse_outgoing_links(zid, body);
+ self.outgoing_links.insert(zid.clone(), links);
+ }
+
+ fn _parse_outgoing_links(zid: &ZettelId, body: &str) -> Vec {
let parser = Parser::new_ext(body, Options::ENABLE_WIKILINKS);
// let mut links = vec![];
@@ -232,6 +238,12 @@ impl Index {
self.zods.get_mut(zid).expect("Invariant broken. Any zid we lookup must exist in the index, otherwise the db is corrupt or not sync'd.")
}
+ pub fn get_links(&self, zid: &ZettelId) -> &Vec {
+ self.outgoing_links
+ .get(zid)
+ .expect("Invariant broken. Any zid we look up exist inside this map")
+ }
+
pub const fn zods(&self) -> &HashMap {
&self.zods
}
diff --git a/src/types/kasten.rs b/src/types/kasten.rs
index 897b2be..f819425 100644
--- a/src/types/kasten.rs
+++ b/src/types/kasten.rs
@@ -112,6 +112,7 @@ impl Kasten {
// and then we sync tags
self.index.sync_tags_with_db(&zid, &self.db).await?;
self.index.sync_zettel_title_with_db(&zid, &self.db).await?;
+ self.index.parse_outgoing_links(&zid);
Ok(())
}
diff --git a/src/types/mod.rs b/src/types/mod.rs
index a5483e2..54f191f 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -35,5 +35,4 @@ mod frontmatter;
pub use frontmatter::FrontMatter;
mod deimos;
-#[expect(unused_imports)]
pub use deimos::Deimos;
diff --git a/src/gui/mod.rs b/src/viz/mod.rs
similarity index 93%
rename from src/gui/mod.rs
rename to src/viz/mod.rs
index 52629aa..8e8ea92 100644
--- a/src/gui/mod.rs
+++ b/src/viz/mod.rs
@@ -73,7 +73,8 @@ impl eframe::App for FilViz {
)
.with_navigations(
&SettingsNavigation::new().with_fit_to_screen_padding(0.2), // .with_zoom_and_pan_enabled(true)
- // .with_fit_to_screen_enabled(false),
+ // .with_fit_to_screen_enabled(false)
+ // ,
);
ui.add(&mut view);
@@ -99,6 +100,10 @@ impl eframe::App for FilViz {
});
}
+ Signal::SetLinks { zid, links } => {
+ self.filaments.set_links_for_zid(&zid, links);
+ }
+
_ => {}
}
}