diff --git a/crates/hyperlog-tui/src/app.rs b/crates/hyperlog-tui/src/app.rs index 3380dcd..8ea556a 100644 --- a/crates/hyperlog-tui/src/app.rs +++ b/crates/hyperlog-tui/src/app.rs @@ -3,7 +3,10 @@ use ratatui::{ widgets::{Block, Borders, Padding, Paragraph}, }; -use crate::{commands::IntoCommand, components::GraphExplorer, state::SharedState, Msg}; +use crate::{ + command_parser::CommandParser, commands::IntoCommand, components::GraphExplorer, + state::SharedState, Msg, +}; use self::{ command_bar::{CommandBar, CommandBarState}, @@ -17,6 +20,14 @@ pub enum Dialog { CreateItem { state: CreateItemState }, } +impl Dialog { + pub fn get_command(&self) -> Option { + match self { + Dialog::CreateItem { state } => state.get_command(), + } + } +} + pub enum Mode { View, Insert, @@ -24,6 +35,8 @@ pub enum Mode { } pub struct App<'a> { + root: String, + state: SharedState, pub mode: Mode, @@ -34,8 +47,13 @@ pub struct App<'a> { } impl<'a> App<'a> { - pub fn new(state: SharedState, graph_explorer: GraphExplorer<'a>) -> Self { + pub fn new( + root: impl Into, + state: SharedState, + graph_explorer: GraphExplorer<'a>, + ) -> Self { Self { + root: root.into(), mode: Mode::View, dialog: None, command: None, @@ -62,8 +80,22 @@ impl<'a> App<'a> { Msg::SubmitCommand { command } => { tracing::info!("submitting command"); - self.command = None; + if let Some(command) = CommandParser::parse(&command) { + if command.is_write() { + if let Some(dialog) = &self.dialog { + if let Some(output) = dialog.get_command() { + self.state.commander.execute(output)?; + } + } + } + self.graph_explorer.update_graph()?; + + if command.is_quit() { + self.dialog = None; + } + } + self.command = None; return Ok(Msg::EnterViewMode.into_command()); } _ => {} @@ -83,8 +115,11 @@ impl<'a> App<'a> { fn open_dialog(&mut self) { if self.dialog.is_none() { + let root = self.root.clone(); + let path = self.graph_explorer.get_current_path(); + self.dialog = Some(Dialog::CreateItem { - state: CreateItemState::default(), + state: CreateItemState::new(root, path), }); } } diff --git a/crates/hyperlog-tui/src/app/dialog.rs b/crates/hyperlog-tui/src/app/dialog.rs index 8441f87..e5ac565 100644 --- a/crates/hyperlog-tui/src/app/dialog.rs +++ b/crates/hyperlog-tui/src/app/dialog.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use ratatui::{prelude::*, widgets::*}; use crate::models::{EditMsg, Msg}; @@ -204,8 +205,10 @@ impl Default for CreateItemFocused { } } -#[derive(Default)] pub struct CreateItemState { + root: String, + path: Vec, + title: InputBuffer, description: InputBuffer, @@ -213,6 +216,20 @@ pub struct CreateItemState { } impl CreateItemState { + pub fn new(root: impl Into, path: impl IntoIterator>) -> Self { + let root = root.into(); + let path = path.into_iter().map(|p| p.into()).collect_vec(); + + Self { + root, + path, + + title: Default::default(), + description: Default::default(), + focused: Default::default(), + } + } + pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> { match &msg { Msg::MoveDown | Msg::MoveUp => match self.focused { @@ -233,6 +250,26 @@ impl CreateItemState { Ok(()) } + + pub fn get_command(&self) -> Option { + let title = self.title.string(); + let description = self.description.string(); + + if !title.is_empty() { + let mut path = self.path.clone(); + path.push(title.replace(' ', "").replace('.', "-")); + + Some(hyperlog_core::commander::Command::CreateItem { + root: self.root.clone(), + path, + title: title.trim().into(), + description: description.trim().into(), + state: hyperlog_core::log::ItemState::NotDone, + }) + } else { + None + } + } } #[derive(Default)] diff --git a/crates/hyperlog-tui/src/command_parser.rs b/crates/hyperlog-tui/src/command_parser.rs new file mode 100644 index 0000000..cd56bf9 --- /dev/null +++ b/crates/hyperlog-tui/src/command_parser.rs @@ -0,0 +1,37 @@ +use itertools::Itertools; + +pub enum Commands { + Write, + Quit, + WriteQuit, +} + +impl Commands { + pub fn is_write(&self) -> bool { + matches!(self, Commands::Write | Commands::WriteQuit) + } + + pub fn is_quit(&self) -> bool { + matches!(self, Commands::Quit | Commands::WriteQuit) + } +} + +pub struct CommandParser {} + +impl CommandParser { + pub fn parse(raw_command: &str) -> Option { + let prepared = raw_command.trim(); + // TODO: respect quotes + let parts = prepared.split_whitespace().collect_vec(); + + match parts.split_first() { + Some((command, _)) => match *command { + "w" | "write" => Some(Commands::Write), + "q" | "quit" => Some(Commands::Quit), + "wq" | "write-quit" => Some(Commands::WriteQuit), + _ => None, + }, + None => None, + } + } +} diff --git a/crates/hyperlog-tui/src/components.rs b/crates/hyperlog-tui/src/components.rs index 9ff7ba6..e6308de 100644 --- a/crates/hyperlog-tui/src/components.rs +++ b/crates/hyperlog-tui/src/components.rs @@ -126,6 +126,17 @@ impl GraphExplorer<'_> { Ok(()) } + + pub(crate) fn get_current_path(&self) -> Vec { + let graph = self.linearize_graph(); + let position_items = &self.inner.current_position; + + if let Some(graph) = graph { + graph.to_current_path(position_items) + } else { + Vec::new() + } + } } trait RenderGraph { @@ -332,6 +343,22 @@ impl MovementGraph { None => Some(self), } } + + fn to_current_path(&self, position_items: &[usize]) -> Vec { + match position_items.split_first() { + Some((first, rest)) => match self.items.get(*first) { + Some(item) => { + let mut current = vec![item.name.clone()]; + let mut next = item.values.to_current_path(rest); + current.append(&mut next); + + current + } + None => Vec::new(), + }, + None => Vec::new(), + } + } } impl From> for MovementGraph { diff --git a/crates/hyperlog-tui/src/lib.rs b/crates/hyperlog-tui/src/lib.rs index 7ef4bfa..61d4292 100644 --- a/crates/hyperlog-tui/src/lib.rs +++ b/crates/hyperlog-tui/src/lib.rs @@ -16,6 +16,7 @@ use crate::{state::SharedState, terminal::TerminalInstance}; pub mod models; pub(crate) mod app; +pub(crate) mod command_parser; pub(crate) mod commands; pub(crate) mod components; pub(crate) mod state; @@ -41,7 +42,7 @@ fn run(terminal: &mut Terminal>, state: SharedState) -> let mut graph_explorer = GraphExplorer::new(state.clone()); graph_explorer.update_graph()?; - let mut app = App::new(state.clone(), graph_explorer); + let mut app = App::new("kjuulh", state.clone(), graph_explorer); loop { terminal.draw(|f| render_app(f, &mut app))?;