From ed5a5db7c52d7460137649ee7ae84517a28f3a3d Mon Sep 17 00:00:00 2001 From: kjuulh Date: Thu, 9 May 2024 00:15:42 +0200 Subject: [PATCH] feat: implement command mode Signed-off-by: kjuulh --- crates/hyperlog-tui/src/app.rs | 67 +++++-- crates/hyperlog-tui/src/app/command_bar.rs | 53 ++++++ crates/hyperlog-tui/src/app/dialog.rs | 40 +++-- crates/hyperlog-tui/src/commands.rs | 31 ++++ crates/hyperlog-tui/src/components.rs | 11 +- crates/hyperlog-tui/src/lib.rs | 48 ++--- crates/hyperlog-tui/src/models.rs | 12 ++ it-is-nice.cast | 200 +++++++++++++++++++++ 8 files changed, 405 insertions(+), 57 deletions(-) create mode 100644 crates/hyperlog-tui/src/app/command_bar.rs create mode 100644 crates/hyperlog-tui/src/commands.rs create mode 100644 it-is-nice.cast diff --git a/crates/hyperlog-tui/src/app.rs b/crates/hyperlog-tui/src/app.rs index b9586fd..1d5f0ef 100644 --- a/crates/hyperlog-tui/src/app.rs +++ b/crates/hyperlog-tui/src/app.rs @@ -3,10 +3,14 @@ use ratatui::{ widgets::{Block, Borders, Padding, Paragraph}, }; -use crate::{components::GraphExplorer, models::EditMsg, state::SharedState, Msg}; +use crate::{commands::IntoCommand, components::GraphExplorer, state::SharedState, Msg}; -use self::dialog::{CreateItem, CreateItemState}; +use self::{ + command_bar::{CommandBar, CommandBarState}, + dialog::{CreateItem, CreateItemState}, +}; +mod command_bar; pub mod dialog; pub enum Dialog { @@ -16,6 +20,7 @@ pub enum Dialog { pub enum Mode { View, Insert, + Command, } pub struct App<'a> { @@ -23,6 +28,7 @@ pub struct App<'a> { pub mode: Mode, dialog: Option, + command: Option, graph_explorer: GraphExplorer<'a>, } @@ -32,12 +38,13 @@ impl<'a> App<'a> { Self { mode: Mode::View, dialog: None, + command: None, state, graph_explorer, } } - pub fn update(&mut self, msg: Msg) -> anyhow::Result<()> { + pub fn update(&mut self, msg: Msg) -> anyhow::Result { tracing::trace!("handling msg: {:?}", msg); match msg { @@ -47,17 +54,31 @@ impl<'a> App<'a> { Msg::MoveUp => self.graph_explorer.move_up()?, Msg::OpenCreateItemDialog => self.open_dialog(), Msg::EnterInsertMode => self.mode = Mode::Insert, - Msg::EnterCommandMode => self.mode = Mode::View, + Msg::EnterViewMode => self.mode = Mode::View, + Msg::EnterCommandMode => { + self.command = Some(CommandBarState::default()); + self.mode = Mode::Command + } + Msg::SubmitCommand => { + tracing::info!("submitting command"); + + self.command = None; + + return Ok(Msg::EnterViewMode.into_command()); + } _ => {} } - if let Some(dialog) = &mut self.dialog { + if let Some(command) = &mut self.command { + let cmd = command.update(&msg)?; + return Ok(cmd.into_command()); + } else if let Some(dialog) = &mut self.dialog { match dialog { Dialog::CreateItem { state } => state.update(&msg)?, } } - Ok(()) + Ok(().into_command()) } fn open_dialog(&mut self) { @@ -106,17 +127,29 @@ pub fn render_app(frame: &mut Frame, state: &mut App) { 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], - ); + match &state.mode { + Mode::View => { + let line = Line::raw("-- VIEW --"); + + let powerbar_block = Block::default() + .borders(Borders::empty()) + .padding(Padding::new(1, 1, 0, 0)); + frame.render_widget(Paragraph::new(vec![line]).block(powerbar_block), chunks[2]); + } + Mode::Insert => { + let line = Line::raw("-- EDIT --"); + + let powerbar_block = Block::default() + .borders(Borders::empty()) + .padding(Padding::new(1, 1, 0, 0)); + frame.render_widget(Paragraph::new(vec![line]).block(powerbar_block), chunks[2]); + } + Mode::Command => { + if let Some(command) = &mut state.command { + frame.render_stateful_widget(CommandBar::default(), chunks[2], command); + } + } + } let Rect { width, height, .. } = chunks[1]; diff --git a/crates/hyperlog-tui/src/app/command_bar.rs b/crates/hyperlog-tui/src/app/command_bar.rs new file mode 100644 index 0000000..24dc88b --- /dev/null +++ b/crates/hyperlog-tui/src/app/command_bar.rs @@ -0,0 +1,53 @@ +use ratatui::widgets::{Paragraph, StatefulWidget, Widget}; + +use crate::{ + commands::IntoCommand, + models::{EditMsg, Msg}, +}; + +use super::dialog::BufferState; + +pub struct CommandBarState { + contents: BufferState, +} + +impl Default for CommandBarState { + fn default() -> Self { + Self { + contents: BufferState::Focused { + content: ropey::Rope::default(), + position: 0, + }, + } + } +} + +#[derive(Default)] +pub struct CommandBar {} + +impl CommandBarState { + pub fn update(&mut self, msg: &Msg) -> anyhow::Result { + if let Msg::Edit(e) = msg { + self.contents.update(e)?; + + if let EditMsg::InsertNewLine = e { + return Ok(Msg::SubmitCommand.into_command()); + } + } + + Ok(().into_command()) + } +} + +impl StatefulWidget for CommandBar { + type State = CommandBarState; + + fn render( + self, + area: ratatui::prelude::Rect, + buf: &mut ratatui::prelude::Buffer, + state: &mut Self::State, + ) { + Paragraph::new(format!(":{}", state.contents.string())).render(area, buf); + } +} diff --git a/crates/hyperlog-tui/src/app/dialog.rs b/crates/hyperlog-tui/src/app/dialog.rs index c7530e8..298a5ce 100644 --- a/crates/hyperlog-tui/src/app/dialog.rs +++ b/crates/hyperlog-tui/src/app/dialog.rs @@ -1,5 +1,3 @@ -use std::{ops::Deref, rc::Rc}; - use ratatui::{prelude::*, widgets::*}; use crate::models::{EditMsg, Msg}; @@ -25,13 +23,13 @@ impl Default for BufferState { } impl BufferState { - fn update(&mut self, msg: &EditMsg) -> anyhow::Result<()> { + pub 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 { + if pos > 0 && pos <= content.len_chars() { content.remove((pos - 1)..pos); *position = position.saturating_sub(1); } @@ -41,8 +39,8 @@ impl BufferState { content.remove((pos)..pos + 1); } } - EditMsg::InsertNewLine => todo!(), - EditMsg::InsertTab => todo!(), + EditMsg::InsertNewLine => {} + EditMsg::InsertTab => {} EditMsg::InsertChar(c) => { content.try_insert_char(pos, *c)?; *position = position.saturating_add(1); @@ -51,7 +49,7 @@ impl BufferState { *position = position.saturating_sub(1); } EditMsg::MoveRight => { - if pos + 1 < content.len_chars() { + if pos < content.len_chars() { *position = pos.saturating_add(1); } } @@ -60,10 +58,17 @@ impl BufferState { Ok(()) } + + pub fn string(&self) -> String { + match self { + BufferState::Focused { content, .. } => content.to_string(), + BufferState::Static { content, .. } => content.to_owned(), + } + } } pub struct InputBuffer { - state: BufferState, + pub state: BufferState, } impl InputBuffer { @@ -111,7 +116,8 @@ impl InputBuffer { pub fn update(&mut self, msg: &Msg) -> anyhow::Result<()> { match msg { Msg::EnterInsertMode => self.to_focused(), - Msg::EnterCommandMode => self.to_static(), + Msg::EnterCommandMode => self.to_focused(), + Msg::EnterViewMode => self.to_static(), Msg::Edit(c) => { self.state.update(c)?; } @@ -120,13 +126,12 @@ impl InputBuffer { Ok(()) } -} -impl Widget for InputBuffer { - fn render(self, area: Rect, buf: &mut Buffer) - where - Self: Sized, - { + pub fn string(&self) -> String { + match &self.state { + BufferState::Focused { ref content, .. } => content.to_string(), + BufferState::Static { content, .. } => content.to_owned(), + } } } @@ -155,10 +160,13 @@ impl<'a> StatefulWidget for InputField<'a> { let block = Block::bordered().title(self.title); match &state.state { - BufferState::Focused { content, .. } => { + BufferState::Focused { content, position } => { Paragraph::new(content.to_string().as_str()) .block(block) .render(area, buf); + + buf.get_mut(area.x + 1 + *position as u16, area.y + 1) + .set_style(Style::new().bg(Color::Magenta).fg(Color::Black)); } BufferState::Static { content, .. } => { Paragraph::new(content.as_str()) diff --git a/crates/hyperlog-tui/src/commands.rs b/crates/hyperlog-tui/src/commands.rs new file mode 100644 index 0000000..680978e --- /dev/null +++ b/crates/hyperlog-tui/src/commands.rs @@ -0,0 +1,31 @@ +use crate::models::Msg; + +pub trait IntoCommand { + fn into_command(self) -> Command; +} + +impl IntoCommand for () { + fn into_command(self) -> Command { + Command::new(|| None) + } +} + +impl IntoCommand for Command { + fn into_command(self) -> Command { + self + } +} + +pub struct Command { + func: Box Option>, +} + +impl Command { + pub fn new Option + 'static>(f: T) -> Self { + Self { func: Box::new(f) } + } + + pub fn execute(self) -> Option { + self.func.call_once(()) + } +} diff --git a/crates/hyperlog-tui/src/components.rs b/crates/hyperlog-tui/src/components.rs index 592d972..9ff7ba6 100644 --- a/crates/hyperlog-tui/src/components.rs +++ b/crates/hyperlog-tui/src/components.rs @@ -151,8 +151,10 @@ impl RenderGraph for MovementGraph { }) { Some((true, rest)) => { if rest.is_empty() { - lines - .push(Line::raw(format!("- {}", item.name)).style(Style::new().bold())); + lines.push( + Line::raw(format!("- {}", item.name)) + .style(Style::new().bold().white()), + ); } else { lines.push( Line::raw(format!("- {}", item.name)) @@ -203,7 +205,10 @@ impl RenderGraph for MovementGraph { Some((true, rest)) => { let mut line = Vec::new(); if rest.is_empty() { - line.push(Span::raw(format!("- {}", item.name)).style(Style::new().bold())); + line.push( + Span::raw(format!("- {}", item.name)) + .style(Style::new().bold().white()), + ); } else { line.push( Span::raw(format!("- {}", item.name)) diff --git a/crates/hyperlog-tui/src/lib.rs b/crates/hyperlog-tui/src/lib.rs index ab4c182..7ef4bfa 100644 --- a/crates/hyperlog-tui/src/lib.rs +++ b/crates/hyperlog-tui/src/lib.rs @@ -1,7 +1,10 @@ +#![feature(fn_traits)] + use std::{io::Stdout, time::Duration}; use anyhow::{Context, Result}; use app::{render_app, App}; +use commands::IntoCommand; use components::GraphExplorer; use crossterm::event::{self, Event, KeyCode}; use hyperlog_core::state::State; @@ -13,6 +16,7 @@ use crate::{state::SharedState, terminal::TerminalInstance}; pub mod models; pub(crate) mod app; +pub(crate) mod commands; pub(crate) mod components; pub(crate) mod state; @@ -67,32 +71,24 @@ 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 &app.mode { + let mut cmd = 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('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') => { + // TODO: batch commands app.update(Msg::OpenCreateItemDialog)?; - app.update(Msg::EnterInsertMode)?; + app.update(Msg::EnterInsertMode)? } - KeyCode::Char('i') => { - app.update(Msg::EnterInsertMode)?; - } - _ => {} + KeyCode::Char('i') => app.update(Msg::EnterInsertMode)?, + KeyCode::Char(':') => app.update(Msg::EnterCommandMode)?, + _ => return Ok(UpdateConclusion(false)), }, - app::Mode::Insert => match key.code { + app::Mode::Command | 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))?, @@ -100,9 +96,19 @@ fn update( 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)?, - _ => {} + KeyCode::Esc => app.update(Msg::EnterViewMode)?, + _ => return Ok(UpdateConclusion(false)), }, + }; + + loop { + let msg = cmd.into_command().execute(); + match msg { + Some(msg) => { + cmd = app.update(msg)?; + } + None => break, + } } } } diff --git a/crates/hyperlog-tui/src/models.rs b/crates/hyperlog-tui/src/models.rs index 9af49fa..56316dd 100644 --- a/crates/hyperlog-tui/src/models.rs +++ b/crates/hyperlog-tui/src/models.rs @@ -1,3 +1,5 @@ +use crate::commands::{Command, IntoCommand}; + #[derive(Debug)] pub enum Msg { MoveRight, @@ -7,10 +9,20 @@ pub enum Msg { OpenCreateItemDialog, EnterInsertMode, + EnterViewMode, EnterCommandMode, + + SubmitCommand, + Edit(EditMsg), } +impl IntoCommand for Msg { + fn into_command(self) -> crate::commands::Command { + Command::new(|| Some(self)) + } +} + #[derive(Debug)] pub enum EditMsg { Delete, diff --git a/it-is-nice.cast b/it-is-nice.cast new file mode 100644 index 0000000..4a711f5 --- /dev/null +++ b/it-is-nice.cast @@ -0,0 +1,200 @@ +{"version": 2, "width": 121, "height": 31, "timestamp": 1715205039, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} +[0.340402, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"] +[0.415094, "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.416098, "o", "\u001b[6 q"] +[0.417144, "o", "\u001b[6 q"] +[0.417386, "o", "\u001b[?2004h"] +[1.099162, "o", "a"] +[1.137079, "o", "\b\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"] +[1.137376, "o", "\b\u001b[1m\u001b[31ma\u001b[0m\u001b[39m\u001b[90msciinema rec it-is-nice.cast\u001b[39m\u001b[28D"] +[1.895435, "o", "\b\u001b[0m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[29D"] +[4.16507, "o", "c"] +[4.170318, "o", "\b\u001b[32mc\u001b[39m"] +[4.205236, "o", "\b\u001b[32mc\u001b[39m\u001b[90mlear\u001b[39m\b\b\b\b"] +[4.314062, "o", "\b\u001b[32mc\u001b[32ma\u001b[39m\u001b[39m \u001b[39m \u001b[39m \b\b\b"] +[4.319326, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"] +[4.339872, "o", "\u001b[90mrgo run\u001b[39m\b\b\b\b\b\b\b"] +[4.375674, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[1m\u001b[31mr\u001b[0m\u001b[39m"] +[4.553571, "o", "\b\u001b[1m\u001b[31mr\u001b[1m\u001b[31mg\u001b[0m\u001b[39m"] +[4.624486, "o", "\b\u001b[1m\u001b[31mg\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"] +[4.628551, "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"] +[4.743312, "o", "\b\u001b[32mo\u001b[32m \u001b[39m"] +[4.745288, "o", "\b\b\u001b[32mo\u001b[39m\u001b[39m "] +[4.85978, "o", "\u001b[39mr"] +[4.863278, "o", "\b\u001b[4mr\u001b[24m"] +[4.923563, "o", "\b\u001b[4mr\u001b[39m\u001b[4mu\u001b[24m"] +[4.927058, "o", "\b\b\u001b[24mr\u001b[24mu"] +[5.064694, "o", "\u001b[39mn"] +[5.322632, "o", "\u001b[?1l\u001b>"] +[5.323055, "o", "\u001b[?2004l"] +[5.330122, "o", "\u001b[0 q"] +[5.330526, "o", "\r\r\n"] +[5.677526, "o", "\u001b[1m\u001b[32m Compiling\u001b[0m hyperlog-tui v0.1.0 (/Users/kah/git/git.front.kjuulh.io/kjuulh/hyperlog/crates/hyperlog-tui)\r\n\u001b[1m\u001b[36m Building\u001b[0m [=======================> ] 315/317: hyperlog-tui \r"] +[5.790434, "o", "\u001b[K\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: methods `toggle` and `string` are 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:99: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;12m74\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;12mmethods 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;12m99\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[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m130\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn string(&self) -> String {\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"] +[5.790526, "o", "\u001b[1m\u001b[36m Building\u001b[0m [=======================> ] 315/317: hyperlog-tui \r"] +[5.964842, "o", "\u001b[K\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m:\u001b[0m `hyperlog-tui` (lib) generated 1 warning\r\n\u001b[1m\u001b[32m Compiling\u001b[0m hyperlog v0.1.0 (/Users/kah/git/git.front.kjuulh.io/kjuulh/hyperlog/crates/hyperlog)\r\n"] +[5.964868, "o", "\u001b[1m\u001b[36m Building\u001b[0m [=======================> ] 316/317: hyperlog(bin) \r"] +[7.029851, "o", "\u001b[K\u001b[1m\u001b[32m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 1.65s\r\n"] +[7.039265, "o", "\u001b[1m\u001b[32m Running\u001b[0m `target/debug/hyperlog`\r\n"] +[7.397795, "o", "\u001b[?1049h"] +[7.400923, "o", "\u001b[1;1H\u001b[38;5;2mhyperlog\u001b[2;1H\u001b[39m─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[3;1H\u001b[38;5;8m- a\u001b[5;3H- b\u001b[7;1H- b\u001b[9;3H- a\u001b[11;3H- b\u001b[13;1H- something\u001b[31;2H\u001b[39m--\u001b[31;5HVIEW\u001b[31;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.659643, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[7.920329, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.183413, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.351457, "o", "\u001b[31;5HEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.609528, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.868582, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[8.942765, "o", "\u001b[31;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.200718, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.461421, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.512177, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.708596, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.905173, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[9.945318, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.09697, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.355132, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.39335, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.651726, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.655332, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.843242, "o", "\u001b[7;1H\u001b[38;5;8m- b\u001b[13;1H\u001b[1m\u001b[38;5;15m- something\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[10.998559, "o", "\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[13;1H\u001b[22m\u001b[38;5;8m- something\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.202965, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[7;1H\u001b[22m\u001b[38;5;8m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.246379, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.412109, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[7;1H\u001b[22m\u001b[38;5;8m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.518186, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.657233, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[7;1H\u001b[22m\u001b[38;5;8m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.743079, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.880111, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[7;1H\u001b[22m\u001b[38;5;8m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[11.945269, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.205525, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.305051, "o", "\u001b[7;1H\u001b[38;5;8m- b\u001b[9;3H\u001b[1m\u001b[38;5;15m- a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.563385, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.587834, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[12.847447, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.056384, "o", "\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[9;3H\u001b[22m\u001b[38;5;8m- a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.073621, "o", "\u001b[7;1H\u001b[38;5;8m- b\u001b[13;1H\u001b[1m\u001b[38;5;15m- something\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.332382, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.590747, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.85099, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[13.941815, "o", "\u001b[13;1H\u001b[38;5;8m- something\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.200606, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.460463, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.549192, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.809497, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[14.94385, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.155403, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.413804, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.578955, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[7;1H\u001b[22m\u001b[38;5;8m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[15.833893, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[5;3H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.097133, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.248338, "o", "\u001b[3;1H\u001b[1m\u001b[38;5;15m- a\u001b[5;3H\u001b[22m\u001b[38;5;8m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.507132, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.572342, "o", "\u001b[3;1H\u001b[38;5;8m- a\u001b[7;1H\u001b[1m\u001b[38;5;15m- b\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[16.830893, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.092909, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.26512, "o", "\u001b[31;1H: \u001b[31;5H \u001b[31;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.524696, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.78239, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[17.887591, "o", "\u001b[31;2Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.146806, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.405362, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.431095, "o", "\u001b[31;2H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.560467, "o", "\u001b[31;2Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.670387, "o", "\u001b[31;3Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.834286, "o", "\u001b[31;4Hm\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[18.927281, "o", "\u001b[31;5He\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.030836, "o", "\u001b[31;6Ht\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.071122, "o", "\u001b[31;7Hh\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.211626, "o", "\u001b[31;8Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.357607, "o", "\u001b[31;9Hn\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.452746, "o", "\u001b[31;10Hg\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.646601, "o", "\u001b[31;1H -- VIEW --\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[19.907225, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.165462, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.423652, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.680919, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.939263, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[20.996048, "o", "\u001b[1;10H~\u001b[1;12Hcreate\u001b[1;19Hitem\u001b[3;1H┌title──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[4;1H│\u001b[38;5;0m\u001b[48;5;5m \u001b[4;121H\u001b[39m\u001b[49m│\u001b[5;1H└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[6;1H┌description─────────────────────────────────────────────────────────────────"] +[20.996104, "o", "───────────────────────────────────────────┐\u001b[7;1H│ \u001b[7;121H│\u001b[8;1H└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[9;3H \u001b[11;3H \u001b[13;1H \u001b[31;5HEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.010383, "o", "\u001b[4;2Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.271922, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.529104, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.790287, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[21.794558, "o", "\u001b[4;2H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.052204, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.18287, "o", "\u001b[4;2Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.189682, "o", "\u001b[4;3Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.211883, "o", "\u001b[4;4Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.214504, "o", "\u001b[4;5Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.284859, "o", "\u001b[4;6Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.319941, "o", "\u001b[4;7Hj\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.342546, "o", "\u001b[4;8Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.424274, "o", "\u001b[4;9Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.455243, "o", "\u001b[4;10Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.517656, "o", "\u001b[4;11Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.521962, "o", "\u001b[4;12Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.553432, "o", "\u001b[4;13Hj\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.56918, "o", "\u001b[4;14Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.612324, "o", "\u001b[4;15Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.692691, "o", "\u001b[4;16Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.723325, "o", "\u001b[4;17Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.738287, "o", "\u001b[4;18Hj\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.765988, "o", "\u001b[4;19Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.826184, "o", "\u001b[4;20Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.900582, "o", "\u001b[4;21Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[22.953845, "o", "\u001b[4;22Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.213295, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.420252, "o", "\u001b[4;23H \u001b[31;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.683026, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[23.815286, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.074879, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.159029, "o", "\u001b[7;2H\u001b[38;5;0m\u001b[48;5;5m \u001b[31;5H\u001b[39m\u001b[49mEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.301429, "o", "\u001b[7;2Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.345464, "o", "\u001b[7;3Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.348744, "o", "\u001b[7;4Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.427224, "o", "\u001b[7;5Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.474234, "o", "\u001b[7;6Hj\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.554142, "o", "\u001b[7;7Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.603048, "o", "\u001b[7;8Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.632587, "o", "\u001b[7;9Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.636099, "o", "\u001b[7;10Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.671615, "o", "\u001b[7;11Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.727905, "o", "\u001b[7;12Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.823809, "o", "\u001b[7;13Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.839083, "o", "\u001b[7;14Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.887115, "o", "\u001b[7;15Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.94834, "o", "\u001b[7;16Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[24.995917, "o", "\u001b[7;17Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.007211, "o", "\u001b[7;18Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.023711, "o", "\u001b[7;19Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.118431, "o", "\u001b[7;20Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.159273, "o", "\u001b[7;21Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.184474, "o", "\u001b[7;22Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.209847, "o", "\u001b[7;23Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.22384, "o", "\u001b[7;24Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.273719, "o", "\u001b[7;25Hf\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.533912, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[25.792562, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[26.052447, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[26.074376, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[26.337157, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[26.426805, "o", "\u001b[7;26H \u001b[31;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[26.689664, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[26.947668, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[27.000511, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[27.258436, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[27.376346, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[27.636429, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[27.895577, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[28.157791, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[28.417062, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[28.459318, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[28.716785, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[28.974774, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"] +[29.024351, "o", "\u001b[?1049l\u001b[?25h"] +[29.026254, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"] +[29.086805, "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[33m23s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"] +[29.087892, "o", "\u001b[6 q"] +[29.088951, "o", "\u001b[6 q"] +[29.089143, "o", "\u001b[?2004h"] +[30.381431, "o", "\u001b[?2004l\r\r\n"]