diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | devbox.json.back | 26 | ||||
| -rw-r--r-- | devbox.lock | 4 | ||||
| -rw-r--r-- | src/app.rs | 73 | ||||
| -rw-r--r-- | src/bibiman.rs | 28 | ||||
| -rw-r--r-- | src/tui/commands.rs | 4 | ||||
| -rw-r--r-- | src/tui/popup.rs | 4 | ||||
| -rw-r--r-- | src/tui/ui.rs | 49 |
8 files changed, 183 insertions, 6 deletions
@@ -7,5 +7,6 @@ res/*.gif res/*.png .direnv +.devbox .envrc devbox.json diff --git a/devbox.json.back b/devbox.json.back new file mode 100644 index 0000000..ff6dc1e --- /dev/null +++ b/devbox.json.back @@ -0,0 +1,26 @@ +{ + "packages": [ + "rustup@latest", + "libiconv@latest", + "openssl@latest" + ], + "shell": { + "init_hook": [ + "projectDir=$PWD/.devbox", + "echo $projectDir", + "rustupHomeDir=\"$projectDir\"/.rustup", + "mkdir -p $rustupHomeDir", + "export RUSTUP_HOME=$rustupHomeDir", + "export LIBRARY_PATH=$LIBRARY_PATH:\"$projectDir/nix/profile/default/lib\"", + "rustup default stable", + "rustup component add rust-src", + "rustup component add rust-analyzer", + "cargo fetch" + ], + "scripts": { + "test": "cargo test -- --show-output", + "start": "cargo run", + "build-docs": "cargo doc" + } + } +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 0000000..4206f37 --- /dev/null +++ b/devbox.lock @@ -0,0 +1,4 @@ +{ + "lockfile_version": "1", + "packages": {} +} @@ -23,6 +23,7 @@ use crate::tui::commands::InputCmdAction; use crate::tui::popup::PopupKind; use crate::tui::{self, Tui}; use crate::{bibiman::Bibiman, tui::commands::CmdAction}; +use ratatui::crossterm::event::KeyCode; use std::ffi::OsStr; use std::path::PathBuf; use std::process::{Command, Stdio}; @@ -78,14 +79,71 @@ impl App { } else if let Some(PopupKind::MessageError) = self.bibiman.popup_area.popup_kind { self.bibiman.close_popup() - } - let command = if self.input_mode { - CmdAction::Input(InputCmdAction::parse(key_event, &self.input)) + } else if let Some(PopupKind::AddEntry) = self.bibiman.popup_area.popup_kind { + // Handle key events for AddEntry popup + match key_event.code { + KeyCode::Char(c) => { + let index = self.bibiman.popup_area.add_entry_cursor_position; + self.bibiman.popup_area.add_entry_input.insert(index, c); + self.bibiman.popup_area.add_entry_cursor_position += 1; + } + KeyCode::Backspace => { + if self.bibiman.popup_area.add_entry_cursor_position > 0 { + self.bibiman.popup_area.add_entry_cursor_position -= 1; + let index = self.bibiman.popup_area.add_entry_cursor_position; + self.bibiman.popup_area.add_entry_input.remove(index); + } + } + KeyCode::Left => { + if self.bibiman.popup_area.add_entry_cursor_position > 0 { + self.bibiman.popup_area.add_entry_cursor_position -= 1; + } + } + KeyCode::Right => { + if self.bibiman.popup_area.add_entry_cursor_position + < self.bibiman.popup_area.add_entry_input.len() + { + self.bibiman.popup_area.add_entry_cursor_position += 1; + } + } + KeyCode::Enter => { + // Handle submission of the new entry + self.bibiman.handle_new_entry_submission(); + self.bibiman.close_popup(); + self.input_mode = false; + } + KeyCode::Esc => { + // Close the popup without saving + self.bibiman.close_popup(); + self.input_mode = false; + } + _ => {} + } } else { - CmdAction::from(key_event) - }; - self.run_command(command, args, &mut tui)? + let command = if self.input_mode { + CmdAction::Input(InputCmdAction::parse(key_event, &self.input)) + } else { + CmdAction::from(key_event) + }; + self.run_command(command, args, &mut tui)? + } } + // Event::Key(key_event) => { + // // Automatically close message popups on next keypress + // if let Some(PopupKind::MessageConfirm) = self.bibiman.popup_area.popup_kind { + // self.bibiman.close_popup() + // } else if let Some(PopupKind::MessageError) = self.bibiman.popup_area.popup_kind + // { + // self.bibiman.close_popup() + // } + + // let command = if self.input_mode { + // CmdAction::Input(InputCmdAction::parse(key_event, &self.input)) + // } else { + // CmdAction::from(key_event) + // }; + // self.run_command(command, args, &mut tui)? + // } Event::Mouse(mouse_event) => { self.run_command(CmdAction::from(mouse_event), args, &mut tui)? } @@ -312,6 +370,9 @@ impl App { } } } + CmdAction::AddEntry => { + self.bibiman.add_entry(); + } CmdAction::ShowHelp => { self.bibiman.show_help(); } diff --git a/src/bibiman.rs b/src/bibiman.rs index bba3eec..ee52207 100644 --- a/src/bibiman.rs +++ b/src/bibiman.rs @@ -23,10 +23,13 @@ use crate::tui::Tui; use crate::{bibiman::entries::EntryTable, bibiman::keywords::TagList}; use arboard::Clipboard; use color_eyre::eyre::{Ok, Result}; +use doi2bib; use editor_command::EditorBuilder; +use futures::executor::block_on; use ratatui::widgets::ScrollbarState; use std::fs; use std::process::Command; +use std::result::Result::Ok as AOk; use tui_input::Input; pub mod bibisetup; @@ -107,6 +110,31 @@ impl Bibiman { self.popup_area.popup_kind = Some(PopupKind::Help); } + pub fn add_entry(&mut self) { + if let CurrentArea::EntryArea = self.current_area { + self.former_area = Some(FormerArea::EntryArea); + } else if let CurrentArea::TagArea = self.current_area { + self.former_area = Some(FormerArea::TagArea); + } + self.popup_area.is_popup = true; + self.current_area = CurrentArea::PopupArea; + self.popup_area.popup_kind = Some(PopupKind::AddEntry); + } + + pub fn handle_new_entry_submission(&mut self) { + let new_entry_title = self.popup_area.add_entry_input.trim(); + let doi2bib = doi2bib::Doi2Bib::new().unwrap(); + let new_entry_future = doi2bib.resolve_doi(new_entry_title); + let new_entry = block_on(new_entry_future); + + if let AOk(entry) = new_entry { + println!("New entry: {:?}", entry); + // Add logic here to integrate the new entry into your application + } else { + self.popup_area.popup_kind = Some(PopupKind::MessageError); + self.popup_area.popup_message = "Failed to add new entry".to_string(); + } + } pub fn close_popup(&mut self) { // Reset all popup fields to default values self.popup_area = PopupArea::default(); diff --git a/src/tui/commands.rs b/src/tui/commands.rs index a3049ee..626b11b 100644 --- a/src/tui/commands.rs +++ b/src/tui/commands.rs @@ -69,6 +69,8 @@ pub enum CmdAction { Exit, // Show keybindings ShowHelp, + // Add new entry + AddEntry, // Do nothing. Nothing, } @@ -118,6 +120,8 @@ impl From<KeyEvent> for CmdAction { KeyCode::PageUp => Self::ScrollInfoUp, // Exit App KeyCode::Char('q') => Self::Exit, + // Add new entry + KeyCode::Char('a') => Self::AddEntry, KeyCode::Char('c') | KeyCode::Char('C') => { if key_event.modifiers == KeyModifiers::CONTROL { Self::Exit diff --git a/src/tui/popup.rs b/src/tui/popup.rs index 890e5c8..9b91801 100644 --- a/src/tui/popup.rs +++ b/src/tui/popup.rs @@ -29,6 +29,7 @@ pub enum PopupKind { MessageConfirm, MessageError, Selection, + AddEntry, } #[derive(Debug, Default)] @@ -39,6 +40,8 @@ pub struct PopupArea { pub popup_scroll_pos: u16, pub popup_list: Vec<String>, pub popup_state: ListState, + pub add_entry_input: String, + pub add_entry_cursor_position: usize, } impl PopupArea { @@ -48,6 +51,7 @@ impl PopupArea { ("TAB: ", "Toggle areas (Entries, Keywords)"), ("/|Ctrl+f: ", "Enter search mode"), ("q|Ctrl+c: ", "Quit bibiman"), + ("a: ", "Add new entry"), ("?: ", "Show help"), ("Entry Table", "sub"), ("j,k|↓,↑: ", "Select next/previous entry"), diff --git a/src/tui/ui.rs b/src/tui/ui.rs index cca87ce..8d7498a 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -181,6 +181,55 @@ pub fn render_popup(app: &mut App, args: &CLIArgs, frame: &mut Frame) { frame.render_widget(Clear, popup_area); frame.render_widget(par, popup_area) } + + Some(PopupKind::AddEntry) => { + let area = frame.area(); + + let block = Block::bordered() + .title_top(" Add Entry ".bold()) + .title_bottom(" (ESC) ━ (ENTER) ".bold()) + .title_alignment(Alignment::Center) + .style( + Style::new() + .fg(Color::Indexed(args.colors.main_text_color)) + .bg(Color::Indexed(args.colors.popup_bg_color)), + ) + .border_set(symbols::border::THICK) + .border_style(Style::new().fg(Color::Indexed(args.colors.entry_color))); + + // Prepare the input fields + let content = vec![ + Line::from(vec![Span::styled( + "DOI: ", + Style::new().fg(Color::Indexed(args.colors.entry_color)), + )]), + Line::from(app.bibiman.popup_area.add_entry_input.clone()), + ]; + let paragraph = Paragraph::new(content) + .block(block.clone()) + .style(Style::new().fg(Color::Indexed(args.colors.main_text_color))) + .wrap(Wrap { trim: false }); + + // Calculate popup size + let popup_width = area.width / 2; + let popup_height = 5; // Adjust as needed + let popup_area = popup_area(area, popup_width, popup_height); + + // Render the popup + frame.render_widget(Clear, popup_area); + frame.render_widget(paragraph, popup_area); + + // Set the cursor position + if app.input_mode { + // Calculate cursor x and y + let input_prefix_len = "Title: ".len() as u16 + 1; // +1 for padding + let cursor_x = popup_area.x + + input_prefix_len + + app.bibiman.popup_area.add_entry_cursor_position as u16; + let cursor_y = popup_area.y + 1; // Line after 'Title: ' + frame.set_cursor_position(Position::new(cursor_x, cursor_y)); + } + } Some(PopupKind::MessageConfirm) => { let area = frame.area(); |
