From 8b9a699ce436425954a25cf181439c44ed87b6c8 Mon Sep 17 00:00:00 2001 From: Willians Faria Date: Sun, 5 May 2024 02:57:39 -0300 Subject: [PATCH] feat: solving some issues with colors and better handling of json --- colors/src/colors.rs | 86 +++++++++---------- reqtui/src/schema/types.rs | 24 +++++- reqtui/src/text_object/text_object.rs | 40 ++++++--- tui/src/components/api_explorer/req_editor.rs | 12 +-- tui/src/components/api_explorer/req_uri.rs | 6 +- tui/src/components/api_explorer/res_viewer.rs | 31 ++++--- tui/src/components/api_explorer/sidebar.rs | 12 +-- tui/src/components/dashboard/dashboard.rs | 2 +- 8 files changed, 125 insertions(+), 88 deletions(-) diff --git a/colors/src/colors.rs b/colors/src/colors.rs index 062db8c..ee3a2a5 100644 --- a/colors/src/colors.rs +++ b/colors/src/colors.rs @@ -21,27 +21,6 @@ impl Default for Colors { } } -fn token_highlight() -> HashMap { - let mut tokens = HashMap::new(); - let colors = BrightColors::default(); - - tokens.insert("conceal".into(), Style::new().fg(colors.red)); - tokens.insert("boolean".into(), Style::new().fg(colors.red)); - tokens.insert("number".into(), Style::new().fg(colors.yellow)); - tokens.insert("property".into(), Style::new().fg(colors.blue)); - tokens.insert( - "punctuation.bracket".into(), - Style::new().fg(colors.magenta), - ); - tokens.insert( - "punctuation.delimiter".into(), - Style::new().fg(colors.magenta), - ); - tokens.insert("string".into(), Style::new().fg(colors.green)); - - tokens -} - #[derive(Debug, PartialEq)] pub struct PrimaryColors { pub foreground: Color, @@ -58,7 +37,7 @@ pub struct NormalColors { pub yellow: Color, pub blue: Color, pub magenta: Color, - pub cyan: Color, + pub orange: Color, pub white: Color, } @@ -70,17 +49,38 @@ pub struct BrightColors { pub yellow: Color, pub blue: Color, pub magenta: Color, - pub cyan: Color, + pub orange: Color, pub white: Color, } +fn token_highlight() -> HashMap { + let mut tokens = HashMap::new(); + let colors = BrightColors::default(); + + tokens.insert("conceal".into(), Style::new().fg(colors.red)); + tokens.insert("boolean".into(), Style::new().fg(colors.red)); + tokens.insert("number".into(), Style::new().fg(colors.magenta)); + tokens.insert("property".into(), Style::new().fg(colors.yellow)); + tokens.insert( + "punctuation.bracket".into(), + Style::new().fg(colors.magenta), + ); + tokens.insert( + "punctuation.delimiter".into(), + Style::new().fg(colors.magenta), + ); + tokens.insert("string".into(), Style::new().fg(colors.green)); + + tokens +} + impl Default for PrimaryColors { fn default() -> Self { PrimaryColors { - foreground: Color::Rgb(0xCE, 0xCE, 0xCE), - background: Color::Rgb(0x0F, 0x14, 0x19), - accent: Color::Rgb(0x12, 0x21, 0x32), - hover: Color::Rgb(0x1A, 0x1F, 0x29), + foreground: Color::Rgb(0x0F, 0x14, 0x19), + background: Color::Rgb(0x18, 0x16, 0x16), + accent: Color::Rgb(0xb6, 0x92, 0x7b), + hover: Color::Rgb(0x38, 0x38, 0x38), } } } @@ -88,14 +88,14 @@ impl Default for PrimaryColors { impl Default for NormalColors { fn default() -> Self { NormalColors { - black: Color::Rgb(0x03, 0x03, 0x03), - red: Color::Rgb(0xD9, 0x57, 0x57), - green: Color::Rgb(0xAA, 0xd9, 0x4C), - yellow: Color::Rgb(0xE6, 0xB4, 0x50), - blue: Color::Rgb(0x59, 0xBA, 0xE6), - magenta: Color::Rgb(0x6C, 0x59, 0x80), - cyan: Color::Rgb(0x95, 0xE6, 0xCB), - white: Color::Rgb(0xBF, 0xBD, 0xB6), + black: Color::Rgb(0x0d, 0x0c, 0x0c), + red: Color::Rgb(0xc4, 0x74, 0x6e), + green: Color::Rgb(0x87, 0xa9, 0x87), + yellow: Color::Rgb(0xc4, 0xb2, 0x8a), + blue: Color::Rgb(0x22, 0x32, 0x49), + magenta: Color::Rgb(0x89, 0x92, 0xa7), + orange: Color::Rgb(0xb6, 0x92, 0x7b), + white: Color::Rgb(0xc5, 0xc9, 0xc5), } } } @@ -103,14 +103,14 @@ impl Default for NormalColors { impl Default for BrightColors { fn default() -> Self { BrightColors { - black: Color::Rgb(0x11, 0x15, 0x1C), - red: Color::Rgb(0xFB, 0x73, 0x73), - green: Color::Rgb(0x7F, 0xD9, 0x4C), - yellow: Color::Rgb(0xE6, 0xB6, 0x73), - blue: Color::Rgb(0x73, 0xB8, 0xFF), - magenta: Color::Rgb(0xD2, 0xA6, 0xFF), - cyan: Color::Rgb(0x95, 0xE6, 0xCB), - white: Color::Rgb(0xFC, 0xFC, 0xFC), + black: Color::Rgb(0x62, 0x5e, 0x5a), + red: Color::Rgb(0xc4, 0x74, 0x6e), + green: Color::Rgb(0x8a, 0x9a, 0x7b), + yellow: Color::Rgb(0xc4, 0xb2, 0x8a), + blue: Color::Rgb(0x8b, 0xa4, 0xb0), + magenta: Color::Rgb(0xa2, 0x92, 0xa3), + orange: Color::Rgb(0xff, 0xa0, 0x66), + white: Color::Rgb(0xff, 0xff, 0xff), } } } diff --git a/reqtui/src/schema/types.rs b/reqtui/src/schema/types.rs index 4ac6212..3fefc31 100644 --- a/reqtui/src/schema/types.rs +++ b/reqtui/src/schema/types.rs @@ -1,6 +1,7 @@ -use std::path::PathBuf; +use std::{hash::Hash, path::PathBuf}; use serde::{Deserialize, Serialize}; +use serde_json::Value; #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] pub struct Schema { @@ -36,11 +37,30 @@ pub enum RequestMethod { Delete, } -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] +impl std::fmt::Display for RequestMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Post => f.write_str("POST"), + Self::Get => f.write_str("GET"), + Self::Put => f.write_str("PUT"), + Self::Patch => f.write_str("PATCH"), + Self::Delete => f.write_str("DELETE"), + } + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct Request { pub method: RequestMethod, pub name: String, pub uri: String, + pub body: Option, +} + +impl Hash for Request { + fn hash(&self, state: &mut H) { + state.write(format!("{}{}{}", self.method, self.name, self.uri).as_bytes()); + } } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] diff --git a/reqtui/src/text_object/text_object.rs b/reqtui/src/text_object/text_object.rs index 4668b46..3436ff2 100644 --- a/reqtui/src/text_object/text_object.rs +++ b/reqtui/src/text_object/text_object.rs @@ -16,6 +16,7 @@ pub struct TextObject { content: Rope, state: std::marker::PhantomData, pub display: Vec>, + pub longest_line: usize, } impl TextObject { @@ -24,6 +25,7 @@ impl TextObject { display: vec![Line::from(content.to_string())], content: Rope::from_str(content), state: std::marker::PhantomData::, + longest_line: 0, } } @@ -32,33 +34,49 @@ impl TextObject { content: self.content, state: std::marker::PhantomData, display: self.display, + longest_line: self.longest_line, } } } +fn build_stylized_line(c: char, i: usize, colors: &[ColorInfo]) -> Span<'static> { + c.to_string().set_style( + colors + .iter() + .find(|color| color.start <= i && color.end >= i) + .map(|c| c.style) + .unwrap_or_default(), + ) +} + impl TextObject { pub fn with_highlight(self, colors: Vec) -> Self { let mut display: Vec = vec![]; let mut current_line: Vec = vec![]; - for (idx, c) in self.to_string().chars().enumerate() { - let style = colors - .iter() - .find(|color| color.start <= idx && color.end >= idx) - .map(|c| c.style) - .unwrap_or_default(); + let mut longest_line = 0; - current_line.push(c.to_string().set_style(style)); + self.to_string() + .chars() + .enumerate() + .for_each(|(i, c)| match c { + '\n' => { + longest_line = longest_line.max(current_line.len()); + current_line.push(build_stylized_line(c, i, &colors)); + display.push(current_line.clone().into()); + current_line.clear(); + } + _ => current_line.push(build_stylized_line(c, i, &colors)), + }); - if c.eq(&'\n') { - display.push(current_line.clone().into()); - current_line.clear(); - } + if !self.to_string().ends_with('\n') { + display.push(current_line.clone().into()); } Self { content: self.content, state: std::marker::PhantomData, display, + longest_line, } } } diff --git a/tui/src/components/api_explorer/req_editor.rs b/tui/src/components/api_explorer/req_editor.rs index 5517614..54e3756 100644 --- a/tui/src/components/api_explorer/req_editor.rs +++ b/tui/src/components/api_explorer/req_editor.rs @@ -86,21 +86,21 @@ impl<'a> ReqEditor<'a> { fn draw_tabs(&self, buf: &mut Buffer, state: &ReqEditorState, size: Rect) { let tabs = Tabs::new(["Request", "Headers", "Query", "Auth"]) - .style(Style::default().fg(self.colors.primary.hover)) + .style(Style::default().fg(self.colors.bright.black)) .select(state.curr_tab.clone().into()) .highlight_style( Style::default() - .fg(self.colors.bright.magenta) - .bg(self.colors.primary.hover), + .fg(self.colors.normal.white) + .bg(self.colors.normal.blue), ); tabs.render(size, buf); } fn draw_container(&self, size: Rect, buf: &mut Buffer, state: &mut ReqEditorState) { let block_border = match (state.is_focused, state.is_selected) { - (true, false) => Style::default().fg(self.colors.bright.magenta), - (true, true) => Style::default().fg(self.colors.bright.yellow), - (_, _) => Style::default().fg(self.colors.primary.hover), + (true, false) => Style::default().fg(self.colors.bright.blue), + (true, true) => Style::default().fg(self.colors.normal.red), + (_, _) => Style::default().fg(self.colors.bright.black), }; let block = Block::default() diff --git a/tui/src/components/api_explorer/req_uri.rs b/tui/src/components/api_explorer/req_uri.rs index 834c6b9..0381d22 100644 --- a/tui/src/components/api_explorer/req_uri.rs +++ b/tui/src/components/api_explorer/req_uri.rs @@ -37,9 +37,9 @@ impl<'a> StatefulWidget for ReqUri<'a> { fn render(self, size: Rect, buf: &mut Buffer, state: &mut Self::State) { let block_border = if state.is_focused { - Style::default().fg(self.colors.bright.magenta) + Style::default().fg(self.colors.bright.blue) } else { - Style::default().fg(self.colors.primary.hover) + Style::default().fg(self.colors.bright.black) }; if let Some(req) = state.selected_request { @@ -50,7 +50,7 @@ impl<'a> StatefulWidget for ReqUri<'a> { .borders(Borders::ALL) .border_style(block_border), ) - .render(size, buf); + .render(size, buf) } } } diff --git a/tui/src/components/api_explorer/res_viewer.rs b/tui/src/components/api_explorer/res_viewer.rs index 6260063..26e045a 100644 --- a/tui/src/components/api_explorer/res_viewer.rs +++ b/tui/src/components/api_explorer/res_viewer.rs @@ -7,7 +7,7 @@ use ratatui::{ text::Line, widgets::{ Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, - Tabs, Widget, Wrap, + Tabs, Widget, }, }; use std::{ @@ -87,9 +87,9 @@ impl<'a> ResViewer<'a> { fn draw_container(&self, size: Rect, buf: &mut Buffer, state: &mut ResViewerState) { let block_border = match (state.is_focused, state.is_selected) { - (true, false) => Style::default().fg(self.colors.bright.magenta), - (true, true) => Style::default().fg(self.colors.bright.yellow), - (_, _) => Style::default().fg(self.colors.primary.hover), + (true, false) => Style::default().fg(self.colors.bright.blue), + (true, true) => Style::default().fg(self.colors.normal.red), + (_, _) => Style::default().fg(self.colors.bright.black), }; let block = Block::default() @@ -101,19 +101,19 @@ impl<'a> ResViewer<'a> { fn draw_tabs(&self, buf: &mut Buffer, state: &ResViewerState, size: Rect) { let tabs = Tabs::new(["Pretty", "Raw", "Cookies", "Headers"]) - .style(Style::default().fg(self.colors.primary.hover)) + .style(Style::default().fg(self.colors.bright.black)) .select(state.curr_tab.clone().into()) .highlight_style( Style::default() - .fg(self.colors.bright.magenta) - .bg(self.colors.primary.hover), + .fg(self.colors.normal.white) + .bg(self.colors.normal.blue), ); tabs.render(size, buf); } - fn draw_preview_tab(&self, state: &mut ResViewerState, buf: &mut Buffer, size: Rect) { + fn draw_current_tab(&self, state: &mut ResViewerState, buf: &mut Buffer, size: Rect) { match state.curr_tab { - ResViewerTabs::Preview => self.draw_preview_response(state, buf, size), + ResViewerTabs::Preview => self.draw_pretty_response(state, buf, size), ResViewerTabs::Raw => self.draw_raw_response(state, buf, size), ResViewerTabs::Cookies => {} ResViewerTabs::Headers => {} @@ -143,7 +143,7 @@ impl<'a> ResViewer<'a> { let lines_in_view = lines .into_iter() .skip(*state.raw_scroll) - .chain(iter::repeat(Line::from("~".fg(self.colors.normal.magenta)))) + .chain(iter::repeat(Line::from("~".fg(self.colors.bright.black)))) .take(size.height.into()) .collect::>(); @@ -162,18 +162,17 @@ impl<'a> ResViewer<'a> { let mut scrollbar_state = ScrollbarState::new(total_ines).position(current_scroll); let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight) - .style(Style::default().fg(self.colors.normal.magenta)) + .style(Style::default().fg(self.colors.normal.red)) .begin_symbol(Some("↑")) .end_symbol(Some("↓")); scrollbar.render(size, buf, &mut scrollbar_state); } - fn draw_preview_response(&self, state: &mut ResViewerState, buf: &mut Buffer, size: Rect) { + fn draw_pretty_response(&self, state: &mut ResViewerState, buf: &mut Buffer, size: Rect) { if let Some(response) = state.response { let lines = response.pretty_body.display.clone(); - // allow for scrolling down until theres only one line left into view if state.raw_scroll.deref().ge(&lines.len().saturating_sub(1)) { *state.raw_scroll = lines.len().saturating_sub(1); } @@ -185,11 +184,11 @@ impl<'a> ResViewer<'a> { let lines_in_view = lines .into_iter() .skip(*state.raw_scroll) - .chain(iter::repeat(Line::from("~".fg(self.colors.normal.magenta)))) + .chain(iter::repeat(Line::from("~".fg(self.colors.bright.black)))) .take(size.height.into()) .collect::>(); - let pretty_response = Paragraph::new(lines_in_view).wrap(Wrap { trim: false }); + let pretty_response = Paragraph::new(lines_in_view); pretty_response.render(request_pane, buf); } } @@ -203,7 +202,7 @@ impl<'a> StatefulWidget for ResViewer<'a> { self.draw_container(size, buf, state); self.draw_tabs(buf, state, layout.tabs_pane); - self.draw_preview_tab(state, buf, layout.content_pane); + self.draw_current_tab(state, buf, layout.content_pane); } } diff --git a/tui/src/components/api_explorer/sidebar.rs b/tui/src/components/api_explorer/sidebar.rs index fb924d1..49e4a6d 100644 --- a/tui/src/components/api_explorer/sidebar.rs +++ b/tui/src/components/api_explorer/sidebar.rs @@ -73,10 +73,10 @@ impl<'a> StatefulWidget for Sidebar<'a> { .collect::>(); let block_border = if state.is_focused { - Style::default().fg(self.colors.bright.magenta) + Style::default().fg(self.colors.bright.blue) } else { // TODO: we need better border colors - Style::default().fg(self.colors.primary.hover) + Style::default().fg(self.colors.bright.black) }; let block = Block::default() @@ -153,10 +153,10 @@ fn build_lines( let req_style = match (is_selected, is_hovered) { (true, true) => Style::default() .fg(colors.normal.yellow) - .bg(colors.primary.accent), + .bg(colors.normal.blue), (true, _) => Style::default() .fg(colors.normal.white) - .bg(colors.primary.accent), + .bg(colors.normal.blue), (_, true) => Style::default() .fg(colors.normal.white) .bg(colors.primary.hover), @@ -183,9 +183,9 @@ fn build_lines( fn colored_method(method: RequestMethod, colors: &colors::Colors) -> Span<'static> { match method { RequestMethod::Get => "GET ".fg(colors.normal.green).bold(), - RequestMethod::Post => "POST ".fg(colors.normal.blue).bold(), + RequestMethod::Post => "POST ".fg(colors.normal.magenta).bold(), RequestMethod::Put => "PUT ".fg(colors.normal.yellow).bold(), - RequestMethod::Patch => "PATCH ".fg(colors.normal.cyan).bold(), + RequestMethod::Patch => "PATCH ".fg(colors.normal.orange).bold(), RequestMethod::Delete => "DELETE".fg(colors.normal.red).bold(), } } diff --git a/tui/src/components/dashboard/dashboard.rs b/tui/src/components/dashboard/dashboard.rs index 1def629..d152a03 100644 --- a/tui/src/components/dashboard/dashboard.rs +++ b/tui/src/components/dashboard/dashboard.rs @@ -503,7 +503,7 @@ impl<'a> Dashboard<'a> { fn draw_title(&self, frame: &mut Frame) -> anyhow::Result<()> { let title = BigText::builder() .pixel_size(PixelSize::Quadrant) - .style(Style::default().fg(self.colors.bright.magenta)) + .style(Style::default().fg(self.colors.normal.red)) .lines(vec!["Select a collection".into()]) .alignment(Alignment::Center) .build()?;