From 26c430e173fae6f568740f66a41c7ba04e770af7 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Tue, 7 May 2024 23:21:13 +0200 Subject: [PATCH] feat: with input Signed-off-by: kjuulh --- Cargo.lock | 17 ++ crates/hyperlog-tui/Cargo.toml | 1 + crates/hyperlog-tui/src/app.rs | 95 ++++++++-- crates/hyperlog-tui/src/app/dialog.rs | 242 ++++++++++++++++++++++++++ crates/hyperlog-tui/src/components.rs | 2 +- crates/hyperlog-tui/src/lib.rs | 53 ++++-- crates/hyperlog-tui/src/models.rs | 16 ++ is-it-vim.cast | 160 +++++++++++++++++ 8 files changed, 557 insertions(+), 29 deletions(-) create mode 100644 crates/hyperlog-tui/src/app/dialog.rs create mode 100644 is-it-vim.cast diff --git a/Cargo.lock b/Cargo.lock index 1da20b0..4d8d259 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,6 +999,7 @@ dependencies = [ "hyperlog-core", "itertools", "ratatui", + "ropey", "serde_json", "similar-asserts", "tokio", @@ -1607,6 +1608,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "rsa" version = "0.9.6" @@ -2165,6 +2176,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_indices" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c" + [[package]] name = "stringprep" version = "0.1.4" diff --git a/crates/hyperlog-tui/Cargo.toml b/crates/hyperlog-tui/Cargo.toml index e43ed9e..75f91d4 100644 --- a/crates/hyperlog-tui/Cargo.toml +++ b/crates/hyperlog-tui/Cargo.toml @@ -18,6 +18,7 @@ ratatui = "0.26.2" crossterm = { version = "0.27.0", features = ["event-stream"] } directories = "5.0.1" human-panic = "2.0.0" +ropey = "1.6.1" [dev-dependencies] similar-asserts = "1.5.0" diff --git a/crates/hyperlog-tui/src/app.rs b/crates/hyperlog-tui/src/app.rs index ae8e089..b9586fd 100644 --- a/crates/hyperlog-tui/src/app.rs +++ b/crates/hyperlog-tui/src/app.rs @@ -3,17 +3,35 @@ use ratatui::{ widgets::{Block, Borders, Padding, Paragraph}, }; -use crate::{components::GraphExplorer, state::SharedState, Msg}; +use crate::{components::GraphExplorer, models::EditMsg, state::SharedState, Msg}; + +use self::dialog::{CreateItem, CreateItemState}; + +pub mod dialog; + +pub enum Dialog { + CreateItem { state: CreateItemState }, +} + +pub enum Mode { + View, + Insert, +} pub struct App<'a> { state: SharedState, + pub mode: Mode, + dialog: Option, + graph_explorer: GraphExplorer<'a>, } impl<'a> App<'a> { pub fn new(state: SharedState, graph_explorer: GraphExplorer<'a>) -> Self { Self { + mode: Mode::View, + dialog: None, state, graph_explorer, } @@ -23,10 +41,30 @@ impl<'a> App<'a> { tracing::trace!("handling msg: {:?}", msg); match msg { - Msg::MoveRight => self.graph_explorer.move_right(), - Msg::MoveLeft => self.graph_explorer.move_left(), - Msg::MoveDown => self.graph_explorer.move_down(), - Msg::MoveUp => self.graph_explorer.move_up(), + Msg::MoveRight => self.graph_explorer.move_right()?, + Msg::MoveLeft => self.graph_explorer.move_left()?, + Msg::MoveDown => self.graph_explorer.move_down()?, + Msg::MoveUp => self.graph_explorer.move_up()?, + Msg::OpenCreateItemDialog => self.open_dialog(), + Msg::EnterInsertMode => self.mode = Mode::Insert, + Msg::EnterCommandMode => self.mode = Mode::View, + _ => {} + } + + if let Some(dialog) = &mut self.dialog { + match dialog { + Dialog::CreateItem { state } => state.update(&msg)?, + } + } + + Ok(()) + } + + fn open_dialog(&mut self) { + if self.dialog.is_none() { + self.dialog = Some(Dialog::CreateItem { + state: CreateItemState::default(), + }); } } } @@ -46,16 +84,40 @@ impl<'a> Widget for &mut App<'a> { } pub fn render_app(frame: &mut Frame, state: &mut App) { - let chunks = - Layout::vertical(vec![Constraint::Length(2), Constraint::Min(0)]).split(frame.size()); + let chunks = Layout::vertical(vec![ + Constraint::Length(2), + Constraint::Min(0), + Constraint::Length(1), + ]) + .split(frame.size()); - let heading = Paragraph::new(text::Line::from( - Span::styled("hyperlog", Style::default()).fg(Color::Green), - )); + let mut heading_parts = vec![Span::styled("hyperlog", Style::default()).fg(Color::Green)]; + + if let Some(dialog) = &state.dialog { + heading_parts.push(Span::raw(" ~ ")); + + match dialog { + Dialog::CreateItem { .. } => heading_parts.push(Span::raw("create item")), + } + } + + let heading = Paragraph::new(text::Line::from(heading_parts)); let block_heading = Block::default().borders(Borders::BOTTOM); frame.render_widget(heading.block(block_heading), chunks[0]); + let powerbar = match &state.mode { + Mode::View => Line::raw("-- VIEW --"), + Mode::Insert => Line::raw("-- EDIT --"), + }; + let powerbar_block = Block::default() + .borders(Borders::empty()) + .padding(Padding::new(1, 1, 0, 0)); + frame.render_widget( + Paragraph::new(vec![powerbar]).block(powerbar_block), + chunks[2], + ); + let Rect { width, height, .. } = chunks[1]; let height = height as usize; @@ -80,7 +142,16 @@ pub fn render_app(frame: &mut Frame, state: &mut App) { top: 2, bottom: 2, }); - //frame.render_widget(background.block(bg_block), chunks[1]); - frame.render_widget(state, chunks[1]) + if let Some(dialog) = state.dialog.as_mut() { + match dialog { + Dialog::CreateItem { state } => { + frame.render_stateful_widget(&mut CreateItem::default(), chunks[1], state) + } + } + + return; + } + + frame.render_widget(state, chunks[1]); } diff --git a/crates/hyperlog-tui/src/app/dialog.rs b/crates/hyperlog-tui/src/app/dialog.rs new file mode 100644 index 0000000..c7530e8 --- /dev/null +++ b/crates/hyperlog-tui/src/app/dialog.rs @@ -0,0 +1,242 @@ +use std::{ops::Deref, rc::Rc}; + +use ratatui::{prelude::*, widgets::*}; + +use crate::models::{EditMsg, Msg}; + +pub enum BufferState { + Focused { + content: ropey::Rope, + position: usize, + }, + Static { + content: String, + position: usize, + }, +} + +impl Default for BufferState { + fn default() -> Self { + Self::Static { + content: String::new(), + position: 0, + } + } +} + +impl BufferState { + fn update(&mut self, msg: &EditMsg) -> anyhow::Result<()> { + if let BufferState::Focused { content, position } = self { + let pos = *position; + + match msg { + EditMsg::Delete => { + if pos > 0 && content.len_chars() > pos { + content.remove((pos - 1)..pos); + *position = position.saturating_sub(1); + } + } + EditMsg::DeleteNext => { + if pos > 0 && pos < content.len_chars() { + content.remove((pos)..pos + 1); + } + } + EditMsg::InsertNewLine => todo!(), + EditMsg::InsertTab => todo!(), + EditMsg::InsertChar(c) => { + content.try_insert_char(pos, *c)?; + *position = position.saturating_add(1); + } + EditMsg::MoveLeft => { + *position = position.saturating_sub(1); + } + EditMsg::MoveRight => { + if pos + 1 < content.len_chars() { + *position = pos.saturating_add(1); + } + } + } + } + + Ok(()) + } +} + +pub struct InputBuffer { + state: BufferState, +} + +impl InputBuffer { + fn to_focused(&mut self) { + match &mut self.state { + BufferState::Focused { .. } => {} + BufferState::Static { content, position } => { + self.state = BufferState::Focused { + content: ropey::Rope::from(content.as_str()), + position: *position, + } + } + } + } + + fn to_static(&mut self) { + match &mut self.state { + BufferState::Focused { content, position } => { + self.state = BufferState::Static { + content: content.to_string(), + position: *position, + } + } + BufferState::Static { .. } => {} + } + } + + pub fn toggle(&mut self) { + match &mut self.state { + BufferState::Focused { content, position } => { + self.state = BufferState::Static { + content: content.to_string(), + position: *position, + } + } + BufferState::Static { content, position } => { + self.state = BufferState::Focused { + content: ropey::Rope::from(content.as_str()), + position: *position, + } + } + } + } + + pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> { + match msg { + Msg::EnterInsertMode => self.to_focused(), + Msg::EnterCommandMode => self.to_static(), + Msg::Edit(c) => { + self.state.update(c)?; + } + _ => {} + } + + Ok(()) + } +} + +impl Widget for InputBuffer { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + } +} + +impl Default for InputBuffer { + fn default() -> Self { + Self { + state: BufferState::default(), + } + } +} + +pub struct InputField<'a> { + title: &'a str, +} + +impl<'a> InputField<'a> { + pub fn new(title: &'a str) -> Self { + Self { title } + } +} + +impl<'a> StatefulWidget for InputField<'a> { + type State = InputBuffer; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + let block = Block::bordered().title(self.title); + + match &state.state { + BufferState::Focused { content, .. } => { + Paragraph::new(content.to_string().as_str()) + .block(block) + .render(area, buf); + } + BufferState::Static { content, .. } => { + Paragraph::new(content.as_str()) + .block(block) + .render(area, buf); + } + } + } +} + +enum CreateItemFocused { + Title, + Description, +} +impl Default for CreateItemFocused { + fn default() -> Self { + Self::Title + } +} + +#[derive(Default)] +pub struct CreateItemState { + title: InputBuffer, + description: InputBuffer, + + focused: CreateItemFocused, +} + +impl CreateItemState { + pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> { + match &msg { + Msg::MoveDown | Msg::MoveUp => match self.focused { + CreateItemFocused::Title => self.focused = CreateItemFocused::Description, + CreateItemFocused::Description => self.focused = CreateItemFocused::Title, + }, + _ => {} + } + + match self.focused { + CreateItemFocused::Title => { + self.title.update(msg)?; + } + CreateItemFocused::Description => { + self.description.update(msg)?; + } + } + + Ok(()) + } +} + +#[derive(Default)] +pub struct CreateItem {} + +impl StatefulWidget for &mut CreateItem { + // fn render(self, area: Rect, buf: &mut Buffer) + // where + // Self: Sized, + // { + // //buf.reset(); + + // // let block = Block::bordered() + // // .title("create item") + // // .padding(Padding::proportional(1)); + + // } + + type State = CreateItemState; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + let chunks = + Layout::vertical(vec![Constraint::Length(3), Constraint::Length(3)]).split(area); + + InputField::new("title").render(chunks[0], buf, &mut state.title); + InputField::new("description").render(chunks[1], buf, &mut state.description); + + // let title = Paragraph::new("something"); //.block(block); + + // title.render(area, buf); + } +} diff --git a/crates/hyperlog-tui/src/components.rs b/crates/hyperlog-tui/src/components.rs index b1d618d..bdfbd0b 100644 --- a/crates/hyperlog-tui/src/components.rs +++ b/crates/hyperlog-tui/src/components.rs @@ -237,7 +237,7 @@ impl<'a> StatefulWidget for GraphExplorer<'a> { fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { let Rect { height, .. } = area; - let height = height as usize; + let _height = height as usize; if let Some(graph) = &state.graph { let movement_graph: MovementGraph = graph.clone().into(); diff --git a/crates/hyperlog-tui/src/lib.rs b/crates/hyperlog-tui/src/lib.rs index e3314f9..ab4c182 100644 --- a/crates/hyperlog-tui/src/lib.rs +++ b/crates/hyperlog-tui/src/lib.rs @@ -5,7 +5,7 @@ use app::{render_app, App}; use components::GraphExplorer; use crossterm::event::{self, Event, KeyCode}; use hyperlog_core::state::State; -use models::Msg; +use models::{EditMsg, Msg}; use ratatui::{backend::CrosstermBackend, Terminal}; use crate::{state::SharedState, terminal::TerminalInstance}; @@ -67,21 +67,42 @@ fn update( ) -> Result { if event::poll(Duration::from_millis(250)).context("event poll failed")? { if let Event::Key(key) = event::read().context("event read failed")? { - match key.code { - KeyCode::Char('q') => return Ok(UpdateConclusion::new(true)), - KeyCode::Char('l') => { - app.update(Msg::MoveRight)?; - } - KeyCode::Char('h') => { - app.update(Msg::MoveLeft)?; - } - KeyCode::Char('j') => { - app.update(Msg::MoveDown)?; - } - KeyCode::Char('k') => { - app.update(Msg::MoveUp)?; - } - _ => {} + match &app.mode { + app::Mode::View => match key.code { + KeyCode::Char('q') => return Ok(UpdateConclusion::new(true)), + KeyCode::Char('l') => { + app.update(Msg::MoveRight)?; + } + KeyCode::Char('h') => { + app.update(Msg::MoveLeft)?; + } + KeyCode::Char('j') => { + app.update(Msg::MoveDown)?; + } + KeyCode::Char('k') => { + app.update(Msg::MoveUp)?; + } + KeyCode::Char('a') => { + app.update(Msg::OpenCreateItemDialog)?; + app.update(Msg::EnterInsertMode)?; + } + KeyCode::Char('i') => { + app.update(Msg::EnterInsertMode)?; + } + _ => {} + }, + + app::Mode::Insert => match key.code { + KeyCode::Backspace => app.update(Msg::Edit(EditMsg::Delete))?, + KeyCode::Enter => app.update(Msg::Edit(EditMsg::InsertNewLine))?, + KeyCode::Tab => app.update(Msg::Edit(EditMsg::InsertTab))?, + KeyCode::Delete => app.update(Msg::Edit(EditMsg::DeleteNext))?, + KeyCode::Char(c) => app.update(Msg::Edit(EditMsg::InsertChar(c)))?, + KeyCode::Left => app.update(Msg::Edit(EditMsg::MoveLeft))?, + KeyCode::Right => app.update(Msg::Edit(EditMsg::MoveRight))?, + KeyCode::Esc => app.update(Msg::EnterCommandMode)?, + _ => {} + }, } } } diff --git a/crates/hyperlog-tui/src/models.rs b/crates/hyperlog-tui/src/models.rs index 6a39256..9af49fa 100644 --- a/crates/hyperlog-tui/src/models.rs +++ b/crates/hyperlog-tui/src/models.rs @@ -4,4 +4,20 @@ pub enum Msg { MoveLeft, MoveDown, MoveUp, + OpenCreateItemDialog, + + EnterInsertMode, + EnterCommandMode, + Edit(EditMsg), +} + +#[derive(Debug)] +pub enum EditMsg { + Delete, + InsertNewLine, + InsertTab, + DeleteNext, + InsertChar(char), + MoveLeft, + MoveRight, } diff --git a/is-it-vim.cast b/is-it-vim.cast new file mode 100644 index 0000000..3c9a3e1 --- /dev/null +++ b/is-it-vim.cast @@ -0,0 +1,160 @@ +{"version": 2, "width": 121, "height": 31, "timestamp": 1715116718, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} +[0.445684, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"] +[0.521553, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0m\u001b[1;31mrs \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"] +[0.52259, "o", "\u001b[6 q"] +[0.52364, "o", "\u001b[6 q"] +[0.523803, "o", "\u001b[?2004h"] +[1.48473, "o", "c"] +[1.488893, "o", "\b\u001b[32mc\u001b[39m"] +[1.521412, "o", "\b\u001b[32mc\u001b[39m\u001b[90mlear\u001b[39m\b\b\b\b"] +[1.607, "o", "\b\u001b[32mc\u001b[32ma\u001b[39m\u001b[39m \u001b[39m \u001b[39m \b\b\b"] +[1.641581, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"] +[1.641876, "o", "\u001b[90mrgo run\u001b[39m\b\b\b\b\b\b\b"] +[1.693323, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[1m\u001b[31mr\u001b[0m\u001b[39m"] +[1.860156, "o", "\b\u001b[1m\u001b[31mr\u001b[1m\u001b[31mg\u001b[0m\u001b[39m"] +[1.951078, "o", "\b\u001b[1m\u001b[31mg\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"] +[1.955652, "o", "\b\b\b\b\b\u001b[0m\u001b[32mc\u001b[0m\u001b[32ma\u001b[0m\u001b[32mr\u001b[0m\u001b[32mg\u001b[0m\u001b[32mo\u001b[39m"] +[2.046422, "o", "\b\u001b[32mo\u001b[32m \u001b[39m"] +[2.049953, "o", "\b\b\u001b[32mo\u001b[39m\u001b[39m "] +[2.157722, "o", "\u001b[39mr"] +[2.162016, "o", "\b\u001b[4mr\u001b[24m"] +[2.249178, "o", "\b\u001b[4mr\u001b[39m\u001b[4mu\u001b[24m"] +[2.252121, "o", "\b\b\u001b[24mr\u001b[24mu"] +[2.396736, "o", "\u001b[39mn"] +[2.675378, "o", "\u001b[?1l\u001b>"] +[2.675763, "o", "\u001b[?2004l"] +[2.68268, "o", "\u001b[0 q"] +[2.683099, "o", "\r\r\n"] +[3.000383, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `models::EditMsg`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-tui/src/app.rs:6:40\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m6\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse crate::{components::GraphExplorer, models::EditMsg, state::SharedState, Msg};\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused imports: `ops::Deref`, `rc::Rc`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-tui/src/app/dialog.rs:1:11\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse std::{ops::Deref, rc::Rc};\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b"] +[3.000413, "o", "[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `area`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-tui/src/app/dialog.rs:126:21\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m126\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m fn render(self, area: Rect, buf: &mut Buffer)\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_area`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\r\n\r\n"] +[3.000492, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `buf`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-tui/src/app/dialog.rs:126:33\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m126\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m fn render(self, area: Rect, buf: &mut Buffer)\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_buf`\u001b[0m\r\n\r\n"] +[3.000542, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: method `toggle` is never used\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-tui/src/app/dialog.rs:94:12\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m69\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mimpl InputBuffer {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mmethod in this implementation\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m94\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn toggle(&mut self) {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(dead_code)]` on by default\u001b[0m\r\n\r\n"] +[3.001057, "o", "\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m:\u001b[0m `hyperlog-tui` (lib) generated 5 warnings (run `cargo fix --lib -p hyperlog-tui` to apply 2 suggestions)\r\n"] +[3.002397, "o", "\u001b[1m\u001b[32m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.27s\r\n"] +[3.009504, "o", "\u001b[1m\u001b[32m Running\u001b[0m `target/debug/hyperlog`\r\n"] +[3.371167, "o", "\u001b[?1049h"] +[3.374559, "o", "\u001b[1;1H\u001b[38;5;2mhyperlog\u001b[2;1H\u001b[39m─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[3;1H-\u001b[3;3Ha\u001b[5;3H-\u001b[5;5Hb\u001b[7;1H-\u001b[7;3Hb\u001b[9;3H-\u001b[9;5Ha\u001b[11;3H-\u001b[11;5Hb\u001b[13;1H-\u001b[13;3Hsomething\u001b[31;2H--\u001b[31;5HVIEW\u001b[31;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[3.631124, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[3.890866, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[3.957644, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[4.147225, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[4.320587, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[4.497276, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[4.603774, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[4.804285, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[4.937156, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.09554, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.200156, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.36907, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.422016, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.628863, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.812175, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[5.977989, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[6.168984, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[6.361295, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[6.538982, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[6.719949, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[6.978025, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[6.988283, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.195593, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.438664, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.629618, "o", "\u001b[3;1H\u001b[1m- a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.887495, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.956448, "o", "\u001b[3;1H- a\u001b[7;1H\u001b[1m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.127701, "o", "\u001b[3;1H\u001b[1m- a\u001b[7;1H\u001b[22m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.348114, "o", "\u001b[3;1H- a\u001b[7;1H\u001b[1m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.542105, "o", "\u001b[3;1H\u001b[1m- a\u001b[7;1H\u001b[22m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.594746, "o", "\u001b[3;1H- a\u001b[7;1H\u001b[1m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.85431, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.885353, "o", "\u001b[7;1H- b\u001b[13;1H\u001b[1m- something\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.142059, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.156051, "o", "\u001b[1;10H~\u001b[1;12Hcreate\u001b[1;19Hitem\u001b[3;1H┌title──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[4;1H│\u001b[4;121H│\u001b[5;1H└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[6;1H┌description──────────────────────────────────────────────────────────────────────────"] +[9.15611, "o", "──────────────────────────────────┐\u001b[7;1H│\u001b[7;3H \u001b[7;121H│\u001b[8;1H└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[9;3H \u001b[9;5H \u001b[11;3H \u001b[11;5H \u001b[13;1H \u001b[31;5HEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.413809, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.671588, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.931216, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.188352, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.24919, "o", "\u001b[4;2Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.509602, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.770123, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.986969, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.245126, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.315903, "o", "\u001b[4;3Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.574888, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.762234, "o", "\u001b[4;4Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.921435, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.183907, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.441825, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.460449, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.598085, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.755283, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.014394, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.275618, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.298695, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.556439, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.589949, "o", "\u001b[4;4H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.752935, "o", "\u001b[4;3H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.897219, "o", "\u001b[4;2H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.156593, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.417759, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.673029, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.785871, "o", "\u001b[4;2Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.882364, "o", "\u001b[4;3Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.04646, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.302315, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.305892, "o", "\u001b[4;5Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.40037, "o", "\u001b[4;6Ht\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.464181, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.720402, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.724438, "o", "\u001b[4;8Hv\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.808499, "o", "\u001b[4;9Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.967497, "o", "\u001b[4;10Hm\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.223665, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.227691, "o", "\u001b[4;11H?\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.485228, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.744166, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.002387, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.246382, "o", "\u001b[31;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.506213, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.647877, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.90637, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.165123, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.424487, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.510633, "o", "\u001b[31;5HEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.770516, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.958742, "o", "\u001b[7;2HN\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.142656, "o", "\u001b[7;3Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.401589, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.416537, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.676891, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.935558, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.978806, "o", "\u001b[7;5Hr\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.019509, "o", "\u001b[7;6He\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.098329, "o", "\u001b[7;7Ha\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.147675, "o", "\u001b[7;8Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.288115, "o", "\u001b[7;9Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.545919, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.549731, "o", "\u001b[7;10Hy\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.80826, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.066166, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.139009, "o", "\u001b[7;11H?\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.3944, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.654169, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.913009, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.170905, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.428891, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.582903, "o", "\u001b[31;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.840988, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.098267, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.356391, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.512109, "o", "\u001b[?1049l\u001b[?25h"] +[23.514873, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"] +[23.574008, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0m\u001b[1;31mrs \u001b[0m\u001b[33m20s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"] +[23.575188, "o", "\u001b[6 q"] +[23.576361, "o", "\u001b[6 q"] +[23.576563, "o", "\u001b[?2004h"] +[24.064638, "o", "c"] +[24.068932, "o", "\b\u001b[32mc\u001b[39m"] +[24.100668, "o", "\b\u001b[32mc\u001b[39m\u001b[90margo run\u001b[39m\u001b[8D"] +[24.597125, "o", "\b\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[9D"] +[24.921733, "o", "\u001b[?2004l\r\r\n"]