···77 q Quit // Quit the application
88 <Ctrl-c> Quit // Another way to quit
99 <Ctrl-z> Suspend // Suspend the application
1010- h Helix
1110 j MoveDown
1211 k MoveUp
1212+ o OpenZettel
1313 }
1414}
+23-12
src/tui/app.rs
···1111use crate::{
1212 config::Config,
1313 tui::{Event, Tui, components::Zk},
1414- types::KastenHandle,
1414+ types::{KastenHandle, ZettelId},
1515};
16161717use super::{components::Component, signal::Signal};
···2626 #[allow(dead_code)]
2727 region: Region,
2828 last_tick_key_events: Vec<KeyEvent>,
2929- _kh: KastenHandle,
2929+ kh: KastenHandle,
3030 signal_tx: UnboundedSender<Signal>,
3131 signal_rx: UnboundedReceiver<Signal>,
3232}
···5757 config: Config::parse()?,
5858 region: Region::default(),
5959 last_tick_key_events: Vec::new(),
6060- _kh: kh,
6060+ kh,
6161 signal_tx,
6262 signal_rx,
6363 })
···160160 debug!("handling signal: {signal:?}");
161161 }
162162163163- match signal {
163163+ match signal.clone() {
164164 Signal::Tick => {
165165 self.last_tick_key_events.drain(..);
166166 }
167167168168 Signal::Quit => self.should_quit = true,
169169170170- Signal::Helix => {
170170+ Signal::Helix { path } => {
171171 tui.exit()?;
172172173173- let hx = spawn(move || -> Result<()> {
174174- Command::new("hx")
175175- .stdin(std::process::Stdio::inherit())
176176- .stdout(std::process::Stdio::inherit())
177177- .stderr(std::process::Stdio::inherit())
178178- .status()?;
173173+ let hx = spawn({
174174+ let path = path.clone();
175175+ move || -> Result<()> {
176176+ Command::new("hx")
177177+ .stdin(std::process::Stdio::inherit())
178178+ .stdout(std::process::Stdio::inherit())
179179+ .stderr(std::process::Stdio::inherit())
180180+ .arg(path)
181181+ .status()?;
179182180180- Ok(())
183183+ Ok(())
184184+ }
181185 });
182186183187 hx.join().unwrap().unwrap();
188188+ // once we get out of the edit, we need to update the zettel for this
189189+ // path and then update the db and the kasten for this stuff
190190+ let zid = ZettelId::try_from(path)?;
191191+192192+ self.kh.write().await.process_zid(&zid).await?;
193193+194194+ self.signal_tx.send(Signal::ClosedZettel)?;
184195185196 tui.terminal.clear()?;
186197 tui.enter()?;
+50-4
src/tui/components/zk/mod.rs
···101101 .expect("must exist");
102102103103 let zettel = kt
104104- .get_by_zettel_id(selected_zettel)
104104+ .get_node_by_zettel_id(selected_zettel)
105105 .expect("must exist, handle case where it doesnt later...")
106106 .payload();
107107···114114115115 // okay now that we have the zettel we need to construct the zettel out of this id
116116 let zettel_view: ZettelView = kt
117117- .get_by_zettel_id(selected_zettel)
117117+ .get_node_by_zettel_id(selected_zettel)
118118 .expect("must exist, handle case where it doesnt later...")
119119 .payload()
120120 .into();
···147147 let kh = self.kh.read().await;
148148149149 self.zettel_view = kh
150150- .get_by_zettel_id(z_id)
150150+ .get_node_by_zettel_id(z_id)
151151 .expect("this should be valid unless the kasten changed out underneath us")
152152 .payload()
153153 .into();
154154155155 self.preview = kh
156156- .get_by_zettel_id(z_id)
156156+ .get_node_by_zettel_id(z_id)
157157 .expect("this should be valid unless the kasten changed out underneath us")
158158 .payload()
159159 .content(&kh.ws)
···182182 self.zettel_list.state.select_previous();
183183 self.update_views_from_zettel_list_selection().await?;
184184 }
185185+186186+ Signal::OpenZettel => {
187187+ let Some(selcted) = self.zettel_list.state.selected() else {
188188+ return Ok(None);
189189+ };
190190+191191+ let Some(zid) = self.zettel_list.id_list.get(selcted) else {
192192+ return Ok(None);
193193+ };
194194+195195+ let kh = self.kh.read().await;
196196+ let path = kh
197197+ .get_node_by_zettel_id(zid)
198198+ .expect(
199199+ "This should not have
200200+ change dout underneath us",
201201+ )
202202+ .payload()
203203+ .absolute_path(&kh.ws);
204204+205205+ drop(kh);
206206+207207+ return Ok(Some(Signal::Helix { path }));
208208+ }
209209+210210+ Signal::ClosedZettel => {
211211+ let selected = self.zettel_list.state.selected().expect(
212212+ "still have to
213213+ figure out what to do if this doesnt exist",
214214+ );
215215+216216+ let Some(id) = self.zettel_list.id_list.get(selected) else {
217217+ return Ok(None);
218218+ };
219219+220220+ let kh = self.kh.read().await;
221221+222222+ let node = kh
223223+ .get_node_by_zettel_id(id)
224224+ .expect("Invariant broken, this must exist.");
225225+226226+ self.zettel_view = ZettelView::from(node.payload());
227227+ self.preview = Preview::from(node.payload().content(&kh.ws).await?);
228228+ drop(kh);
229229+ }
230230+185231 _ => {}
186232 }
187233 Ok(None)
+13-3
src/tui/signal.rs
···11-use std::str::FromStr;
11+use std::{path::PathBuf, str::FromStr};
2233use color_eyre::eyre::eyre;
44use strum::Display;
···1717 ClearScreen,
1818 Error(String),
1919 Help,
2020+2121+ // movement
2022 MoveDown,
2123 MoveUp,
22242525+ /// User asks to open a `Zettel`
2626+ OpenZettel,
2727+2828+ /// The user is done editing a `Zettel`
2929+ ClosedZettel,
3030+2331 /// this is fucking temporary
2424- Helix,
3232+ Helix {
3333+ path: PathBuf,
3434+ },
2535}
26362737impl FromStr for Signal {
···3242 "suspend" => Self::Suspend,
3343 "resume" => Self::Resume,
3444 "quit" => Self::Quit,
3535- "helix" => Self::Helix,
3645 "movedown" => Self::MoveDown,
3746 "moveup" => Self::MoveUp,
4747+ "openzettel" => Self::OpenZettel,
3848 _ => {
3949 return Err(eyre!(format!(
4050 "Attempt to construct a non-user Signal from str: {s}"
+31-1
src/types/kasten.rs
···104104 most_recently_edited: None,
105105 })
106106 }
107107- pub fn get_by_zettel_id(&self, id: &ZettelId) -> Option<&Node<Zettel, Link>> {
107107+108108+ /// processes the `Zettel` for the provided `ZettelId`,
109109+ /// meaning it updates the internal state of the `Kasten`
110110+ /// with the changes in `Zettel`.
111111+ pub async fn process_zid(&mut self, zid: &ZettelId) -> Result<()> {
112112+ //NOTE: need to clone to get around borrowing rules but
113113+ // ideally we dont have to do this, kind of cringe imo.
114114+ let ws = self.ws.clone();
115115+116116+ let zettel = self
117117+ .get_node_by_zettel_id_mut(zid)
118118+ .expect("this should not happen ever")
119119+ .payload_mut();
120120+121121+ zettel.sync_with_file(&ws).await?;
122122+123123+ Ok(())
124124+ }
125125+126126+ pub fn get_node_by_zettel_id(&self, id: &ZettelId) -> Option<&Node<Zettel, Link>> {
108127 let idx = self.zid_to_gid.get(id)?;
109128110129 let node = self.graph.node(*idx).expect(
111130 "invariant broken if internal hashmap is not uptodate with
112131 the state of the graph...",
113132 );
133133+ Some(node)
134134+ }
135135+136136+ pub fn get_node_by_zettel_id_mut(&mut self, id: &ZettelId) -> Option<&mut Node<Zettel, Link>> {
137137+ let idx = self.zid_to_gid.get(id)?;
138138+139139+ let node = self.graph.node_mut(*idx).expect(
140140+ "invariant broken if internal hashmap is not uptodate with the
141141+ state of the graph...",
142142+ );
143143+114144 Some(node)
115145 }
116146}
+16-4
src/types/zettel.rs
···11use dto::{
22 ActiveModelTrait, ActiveValue, ColumnTrait, DateTime, EntityTrait as _, IntoActiveModel,
33- QueryFilter, TagActiveModel, TagEntity, ZettelActiveModel, ZettelEntity, ZettelModelEx,
44- ZettelTagActiveModel, ZettelTagColumns, ZettelTagEntity,
33+ QueryFilter, TagActiveModel, TagEntity, ZettelActiveModel, ZettelActiveModelEx, ZettelEntity,
44+ ZettelModelEx, ZettelTagActiveModel, ZettelTagColumns, ZettelTagEntity,
55};
66use pulldown_cmark::{Event, Parser, Tag as MkTag};
77use serde::{Deserialize, Serialize};
···1515use dto::NanoId;
1616use tokio::{fs::File, io::AsyncWriteExt};
17171818-use crate::types::{FrontMatter, Link, Tag, Workspace, frontmatter};
1818+use crate::types::{FrontMatter, Kasten, Link, Tag, Workspace, frontmatter};
19192020/// A `Zettel` is a note about a single idea.
2121/// It can have many `Tag`s, just meaning it can fall under many
···7979 Ok(zettel.into())
8080 }
81818282+ pub async fn sync_with_file(&mut self, ws: &Workspace) -> Result<()> {
8383+ let (fm, content) = FrontMatter::extract_from_file(self.absolute_path(ws)).await?;
8484+8585+ self.title = fm.title;
8686+8787+ // it could have new tags and stuff...
8888+8989+ // todo!();
9090+9191+ Ok(())
9292+ }
9393+8294 /// Returns the most up-to-date `FrontMatter` for this
8395 /// `Zettel`
8496 pub async fn front_matter(&self, ws: &Workspace) -> Result<FrontMatter> {
···101113 Ok(File::open(path).await?)
102114 }
103115104104- fn absolute_path(&self, ws: &Workspace) -> PathBuf {
116116+ pub fn absolute_path(&self, ws: &Workspace) -> PathBuf {
105117 ws.root.clone().join(&self.file_path)
106118 }
107119