feat: tie dialog to graph
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
7cead58ed3
commit
547d34782c
@ -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<hyperlog_core::commander::Command> {
|
||||
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<String>,
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<String>,
|
||||
|
||||
title: InputBuffer,
|
||||
description: InputBuffer,
|
||||
|
||||
@ -213,6 +216,20 @@ pub struct CreateItemState {
|
||||
}
|
||||
|
||||
impl CreateItemState {
|
||||
pub fn new(root: impl Into<String>, path: impl IntoIterator<Item = impl Into<String>>) -> 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<hyperlog_core::commander::Command> {
|
||||
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)]
|
||||
|
37
crates/hyperlog-tui/src/command_parser.rs
Normal file
37
crates/hyperlog-tui/src/command_parser.rs
Normal file
@ -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<Commands> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
@ -126,6 +126,17 @@ impl GraphExplorer<'_> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_current_path(&self) -> Vec<String> {
|
||||
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<String> {
|
||||
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<Box<GraphItem>> for MovementGraph {
|
||||
|
@ -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<CrosstermBackend<Stdout>>, 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))?;
|
||||
|
Loading…
Reference in New Issue
Block a user