mirror of
https://github.com/atuinsh/atuin.git
synced 2024-11-22 00:03:49 +01:00
initial implementation of customizable styles for tui
This commit is contained in:
parent
22a9b497ad
commit
dd587201ca
44
Cargo.lock
generated
44
Cargo.lock
generated
@ -249,6 +249,7 @@ dependencies = [
|
||||
"parse_duration",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"ratatui",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rmp",
|
||||
@ -563,6 +564,15 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.88"
|
||||
@ -720,6 +730,20 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.13.4"
|
||||
@ -2581,17 +2605,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.25.0"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb"
|
||||
checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"serde",
|
||||
"stability",
|
||||
"strum",
|
||||
"unicode-segmentation",
|
||||
@ -3465,6 +3491,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.6"
|
||||
@ -3496,18 +3528,18 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.25.0"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||
checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.25.3"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
|
||||
checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -47,6 +47,7 @@ typed-builder = "0.18.0"
|
||||
pretty_assertions = "1.3.0"
|
||||
thiserror = "1.0"
|
||||
rustix = {version = "0.38.30", features=["process", "fs"]}
|
||||
ratatui = { version = "0.26", features = ["serde"] }
|
||||
|
||||
[workspace.dependencies.reqwest]
|
||||
version = "0.11"
|
||||
|
@ -54,6 +54,7 @@ futures = "0.3"
|
||||
crypto_secretbox = "0.1.1"
|
||||
generic-array = { version = "0.14", features = ["serde"] }
|
||||
serde_with = "3.5.1"
|
||||
ratatui = { workspace = true }
|
||||
|
||||
# encryption
|
||||
rusty_paseto = { version = "0.6.0", default-features = false }
|
||||
|
@ -15,9 +15,10 @@ use config::{
|
||||
use eyre::{bail, eyre, Context, Error, Result};
|
||||
use fs_err::{create_dir_all, File};
|
||||
use parse_duration::parse;
|
||||
use ratatui::style::{Color, Stylize};
|
||||
use regex::RegexSet;
|
||||
use semver::Version;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde_with::DeserializeFromStr;
|
||||
use time::{
|
||||
format_description::{well_known::Rfc3339, FormatItem},
|
||||
@ -321,6 +322,107 @@ impl Default for Stats {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
pub struct Styles {
|
||||
#[serde(default, deserialize_with = "Variants::deserialize_style")]
|
||||
pub command: Option<ratatui::style::Style>,
|
||||
#[serde(default, deserialize_with = "Variants::deserialize_style")]
|
||||
pub command_selected: Option<ratatui::style::Style>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Variants {
|
||||
Color(Color),
|
||||
Components(Components),
|
||||
}
|
||||
|
||||
impl Variants {
|
||||
fn deserialize_style<'de, D>(deserializer: D) -> Result<Option<ratatui::style::Style>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let variants: Option<Variants> = Deserialize::deserialize(deserializer)?;
|
||||
let style: Option<ratatui::style::Style> = variants.map(|variants| variants.into());
|
||||
|
||||
Ok(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Variants> for ratatui::style::Style {
|
||||
fn from(value: Variants) -> ratatui::style::Style {
|
||||
match value {
|
||||
Variants::Components(complex_style) => complex_style.into(),
|
||||
Variants::Color(color) => color.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct Components {
|
||||
// Colors
|
||||
#[serde(default)]
|
||||
pub foreground: Option<Color>,
|
||||
#[serde(default)]
|
||||
pub background: Option<Color>,
|
||||
#[serde(default)]
|
||||
pub underline: Option<Color>,
|
||||
|
||||
// Modifiers
|
||||
#[serde(default)]
|
||||
pub bold: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub crossed_out: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub italic: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub underlined: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<Components> for ratatui::style::Style {
|
||||
fn from(value: Components) -> ratatui::style::Style {
|
||||
let mut style = ratatui::style::Style::default();
|
||||
|
||||
if let Some(color) = value.foreground {
|
||||
style = style.fg(color);
|
||||
};
|
||||
|
||||
if let Some(color) = value.background {
|
||||
style = style.bg(color);
|
||||
}
|
||||
|
||||
if let Some(color) = value.underline {
|
||||
style = style.underline_color(color);
|
||||
}
|
||||
|
||||
style = match value.bold {
|
||||
Some(true) => style.bold(),
|
||||
Some(_) => style.not_bold(),
|
||||
_ => style,
|
||||
};
|
||||
|
||||
style = match value.crossed_out {
|
||||
Some(true) => style.crossed_out(),
|
||||
Some(_) => style.not_crossed_out(),
|
||||
_ => style,
|
||||
};
|
||||
|
||||
style = match value.italic {
|
||||
Some(true) => style.italic(),
|
||||
Some(_) => style.not_italic(),
|
||||
_ => style,
|
||||
};
|
||||
|
||||
style = match value.underlined {
|
||||
Some(true) => style.underlined(),
|
||||
Some(_) => style.not_underlined(),
|
||||
_ => style,
|
||||
};
|
||||
|
||||
style
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
pub struct Sync {
|
||||
pub records: bool,
|
||||
@ -382,6 +484,9 @@ pub struct Settings {
|
||||
#[serde(default)]
|
||||
pub stats: Stats,
|
||||
|
||||
#[serde(default)]
|
||||
pub styles: Styles,
|
||||
|
||||
#[serde(default)]
|
||||
pub sync: Sync,
|
||||
|
||||
|
@ -74,7 +74,7 @@ tiny-bip39 = "1"
|
||||
futures-util = "0.3"
|
||||
fuzzy-matcher = "0.3.7"
|
||||
colored = "2.0.4"
|
||||
ratatui = "0.25"
|
||||
ratatui = { workspace = true }
|
||||
tracing = "0.1"
|
||||
cli-clipboard = { version = "0.4.0", optional = true }
|
||||
uuid = { workspace = true }
|
||||
|
@ -1,11 +1,11 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use atuin_client::history::History;
|
||||
use atuin_client::{history::History, settings::Styles};
|
||||
use atuin_common::utils::Escapable as _;
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Color, Modifier, Style},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
widgets::{Block, StatefulWidget, Widget},
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
@ -19,6 +19,7 @@ pub struct HistoryList<'a> {
|
||||
/// Apply an alternative highlighting to the selected row
|
||||
alternate_highlight: bool,
|
||||
now: &'a dyn Fn() -> OffsetDateTime,
|
||||
styles: &'a Styles,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -70,6 +71,7 @@ impl<'a> StatefulWidget for HistoryList<'a> {
|
||||
inverted: self.inverted,
|
||||
alternate_highlight: self.alternate_highlight,
|
||||
now: &self.now,
|
||||
styles: self.styles,
|
||||
};
|
||||
|
||||
for item in self.history.iter().skip(state.offset).take(end - start) {
|
||||
@ -91,6 +93,7 @@ impl<'a> HistoryList<'a> {
|
||||
inverted: bool,
|
||||
alternate_highlight: bool,
|
||||
now: &'a dyn Fn() -> OffsetDateTime,
|
||||
styles: &'a Styles,
|
||||
) -> Self {
|
||||
Self {
|
||||
history,
|
||||
@ -98,6 +101,7 @@ impl<'a> HistoryList<'a> {
|
||||
inverted,
|
||||
alternate_highlight,
|
||||
now,
|
||||
styles,
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +134,7 @@ struct DrawState<'a> {
|
||||
inverted: bool,
|
||||
alternate_highlight: bool,
|
||||
now: &'a dyn Fn() -> OffsetDateTime,
|
||||
styles: &'a Styles,
|
||||
}
|
||||
|
||||
// longest line prefix I could come up with
|
||||
@ -183,12 +188,16 @@ impl DrawState<'_> {
|
||||
}
|
||||
|
||||
fn command(&mut self, h: &History) {
|
||||
let mut style = Style::default();
|
||||
if !self.alternate_highlight && (self.y as usize + self.state.offset == self.state.selected)
|
||||
{
|
||||
// if not applying alternative highlighting to the whole row, color the command
|
||||
style = style.fg(Color::Red).add_modifier(Modifier::BOLD);
|
||||
}
|
||||
let alternate_highlight = self.alternate_highlight;
|
||||
let selected = self.y as usize + self.state.offset == self.state.selected;
|
||||
|
||||
let style = if !alternate_highlight && selected {
|
||||
self.styles
|
||||
.command_selected
|
||||
.unwrap_or_else(|| Style::default().fg(Color::Red).bold())
|
||||
} else {
|
||||
self.styles.command.unwrap_or_default()
|
||||
};
|
||||
|
||||
for section in h.command.escape_control().split_ascii_whitespace() {
|
||||
self.draw(" ", style);
|
||||
|
@ -22,7 +22,7 @@ use unicode_width::UnicodeWidthStr;
|
||||
use atuin_client::{
|
||||
database::{current_context, Database},
|
||||
history::{store::HistoryStore, History, HistoryStats},
|
||||
settings::{CursorStyle, ExitMode, FilterMode, KeymapMode, SearchMode, Settings},
|
||||
settings::{CursorStyle, ExitMode, FilterMode, KeymapMode, SearchMode, Settings, Styles},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -557,7 +557,7 @@ impl State {
|
||||
// TODO: this should be split so that we have one interactive search container that is
|
||||
// EITHER a search box or an inspector. But I'm not doing that now, way too much atm.
|
||||
// also allocate less 🙈
|
||||
let titles = TAB_TITLES.iter().copied().map(Line::from).collect();
|
||||
let titles: Vec<_> = TAB_TITLES.iter().copied().map(Line::from).collect();
|
||||
|
||||
let tabs = Tabs::new(titles)
|
||||
.block(Block::default().borders(Borders::NONE))
|
||||
@ -596,8 +596,13 @@ impl State {
|
||||
|
||||
match self.tab_index {
|
||||
0 => {
|
||||
let results_list =
|
||||
Self::build_results_list(style, results, self.keymap_mode, &self.now);
|
||||
let results_list = Self::build_results_list(
|
||||
style,
|
||||
results,
|
||||
self.keymap_mode,
|
||||
&self.now,
|
||||
&settings.styles,
|
||||
);
|
||||
f.render_stateful_widget(results_list, results_list_chunk, &mut self.results_state);
|
||||
}
|
||||
|
||||
@ -718,12 +723,14 @@ impl State {
|
||||
results: &'a [History],
|
||||
keymap_mode: KeymapMode,
|
||||
now: &'a dyn Fn() -> OffsetDateTime,
|
||||
styles: &'a Styles,
|
||||
) -> HistoryList<'a> {
|
||||
let results_list = HistoryList::new(
|
||||
results,
|
||||
style.invert,
|
||||
keymap_mode == KeymapMode::VimNormal,
|
||||
now,
|
||||
styles,
|
||||
);
|
||||
|
||||
if style.compact {
|
||||
|
Loading…
Reference in New Issue
Block a user