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},
|
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::{
|
use self::{
|
||||||
command_bar::{CommandBar, CommandBarState},
|
command_bar::{CommandBar, CommandBarState},
|
||||||
@ -17,6 +20,14 @@ pub enum Dialog {
|
|||||||
CreateItem { state: CreateItemState },
|
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 {
|
pub enum Mode {
|
||||||
View,
|
View,
|
||||||
Insert,
|
Insert,
|
||||||
@ -24,6 +35,8 @@ pub enum Mode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
|
root: String,
|
||||||
|
|
||||||
state: SharedState,
|
state: SharedState,
|
||||||
|
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
@ -34,8 +47,13 @@ pub struct App<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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 {
|
Self {
|
||||||
|
root: root.into(),
|
||||||
mode: Mode::View,
|
mode: Mode::View,
|
||||||
dialog: None,
|
dialog: None,
|
||||||
command: None,
|
command: None,
|
||||||
@ -62,8 +80,22 @@ impl<'a> App<'a> {
|
|||||||
Msg::SubmitCommand { command } => {
|
Msg::SubmitCommand { command } => {
|
||||||
tracing::info!("submitting 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());
|
return Ok(Msg::EnterViewMode.into_command());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -83,8 +115,11 @@ impl<'a> App<'a> {
|
|||||||
|
|
||||||
fn open_dialog(&mut self) {
|
fn open_dialog(&mut self) {
|
||||||
if self.dialog.is_none() {
|
if self.dialog.is_none() {
|
||||||
|
let root = self.root.clone();
|
||||||
|
let path = self.graph_explorer.get_current_path();
|
||||||
|
|
||||||
self.dialog = Some(Dialog::CreateItem {
|
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 ratatui::{prelude::*, widgets::*};
|
||||||
|
|
||||||
use crate::models::{EditMsg, Msg};
|
use crate::models::{EditMsg, Msg};
|
||||||
@ -204,8 +205,10 @@ impl Default for CreateItemFocused {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct CreateItemState {
|
pub struct CreateItemState {
|
||||||
|
root: String,
|
||||||
|
path: Vec<String>,
|
||||||
|
|
||||||
title: InputBuffer,
|
title: InputBuffer,
|
||||||
description: InputBuffer,
|
description: InputBuffer,
|
||||||
|
|
||||||
@ -213,6 +216,20 @@ pub struct CreateItemState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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<()> {
|
pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> {
|
||||||
match &msg {
|
match &msg {
|
||||||
Msg::MoveDown | Msg::MoveUp => match self.focused {
|
Msg::MoveDown | Msg::MoveUp => match self.focused {
|
||||||
@ -233,6 +250,26 @@ impl CreateItemState {
|
|||||||
|
|
||||||
Ok(())
|
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)]
|
#[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(())
|
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 {
|
trait RenderGraph {
|
||||||
@ -332,6 +343,22 @@ impl MovementGraph {
|
|||||||
None => Some(self),
|
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 {
|
impl From<Box<GraphItem>> for MovementGraph {
|
||||||
|
@ -16,6 +16,7 @@ use crate::{state::SharedState, terminal::TerminalInstance};
|
|||||||
pub mod models;
|
pub mod models;
|
||||||
|
|
||||||
pub(crate) mod app;
|
pub(crate) mod app;
|
||||||
|
pub(crate) mod command_parser;
|
||||||
pub(crate) mod commands;
|
pub(crate) mod commands;
|
||||||
pub(crate) mod components;
|
pub(crate) mod components;
|
||||||
pub(crate) mod state;
|
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());
|
let mut graph_explorer = GraphExplorer::new(state.clone());
|
||||||
graph_explorer.update_graph()?;
|
graph_explorer.update_graph()?;
|
||||||
|
|
||||||
let mut app = App::new(state.clone(), graph_explorer);
|
let mut app = App::new("kjuulh", state.clone(), graph_explorer);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| render_app(f, &mut app))?;
|
terminal.draw(|f| render_app(f, &mut app))?;
|
||||||
|
Loading…
Reference in New Issue
Block a user