mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-07 03:55:23 +00:00
feat(ui): add support for standard ANSI colors theming and update default theme (#221)
Fixes #211 <img width="2554" alt="Screenshot 2025-01-06 at 13 01 57" src="https://github.com/user-attachments/assets/3706b93a-4be1-4a88-8c3e-bfb71a36db50" />
This commit is contained in:
parent
53bd4a3815
commit
d7e6c35735
@ -56,7 +56,7 @@ input_bar_position = "top"
|
|||||||
# A list of builtin themes can be found in the `themes` directory of the television
|
# A list of builtin themes can be found in the `themes` directory of the television
|
||||||
# repository. You may also create your own theme by creating a new file in a `themes`
|
# repository. You may also create your own theme by creating a new file in a `themes`
|
||||||
# directory in your configuration directory (see the `config.toml` location above).
|
# directory in your configuration directory (see the `config.toml` location above).
|
||||||
theme = "catppuccin"
|
theme = "default"
|
||||||
|
|
||||||
# Previewers settings
|
# Previewers settings
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
@ -65,7 +65,7 @@ theme = "catppuccin"
|
|||||||
# Bulitin syntax highlighting uses the same syntax highlighting engine as bat.
|
# Bulitin syntax highlighting uses the same syntax highlighting engine as bat.
|
||||||
# To get a list of your currently available themes, run `bat --list-themes`
|
# To get a list of your currently available themes, run `bat --list-themes`
|
||||||
# Note that setting the BAT_THEME environment variable will override this setting.
|
# Note that setting the BAT_THEME environment variable will override this setting.
|
||||||
theme = "Coldark-Dark"
|
theme = "TwoDark"
|
||||||
|
|
||||||
# Keybindings
|
# Keybindings
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
@ -12,14 +12,68 @@ use super::get_config_dir;
|
|||||||
|
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Color {
|
pub enum Color {
|
||||||
|
Ansi(ANSIColor),
|
||||||
|
Rgb(RGBColor),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
pub fn from_str(s: &str) -> Option<Self> {
|
||||||
|
if s.starts_with('#') {
|
||||||
|
RGBColor::from_str(s).map(Self::Rgb)
|
||||||
|
} else {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"black" => Some(Self::Ansi(ANSIColor::Black)),
|
||||||
|
"red" => Some(Self::Ansi(ANSIColor::Red)),
|
||||||
|
"green" => Some(Self::Ansi(ANSIColor::Green)),
|
||||||
|
"yellow" => Some(Self::Ansi(ANSIColor::Yellow)),
|
||||||
|
"blue" => Some(Self::Ansi(ANSIColor::Blue)),
|
||||||
|
"magenta" => Some(Self::Ansi(ANSIColor::Magenta)),
|
||||||
|
"cyan" => Some(Self::Ansi(ANSIColor::Cyan)),
|
||||||
|
"white" => Some(Self::Ansi(ANSIColor::White)),
|
||||||
|
"bright-black" => Some(Self::Ansi(ANSIColor::BrightBlack)),
|
||||||
|
"bright-red" => Some(Self::Ansi(ANSIColor::BrightRed)),
|
||||||
|
"bright-green" => Some(Self::Ansi(ANSIColor::BrightGreen)),
|
||||||
|
"bright-yellow" => Some(Self::Ansi(ANSIColor::BrightYellow)),
|
||||||
|
"bright-blue" => Some(Self::Ansi(ANSIColor::BrightBlue)),
|
||||||
|
"bright-magenta" => Some(Self::Ansi(ANSIColor::BrightMagenta)),
|
||||||
|
"bright-cyan" => Some(Self::Ansi(ANSIColor::BrightCyan)),
|
||||||
|
"bright-white" => Some(Self::Ansi(ANSIColor::BrightWhite)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ANSIColor {
|
||||||
|
Black,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Yellow,
|
||||||
|
Blue,
|
||||||
|
Magenta,
|
||||||
|
Cyan,
|
||||||
|
White,
|
||||||
|
BrightBlack,
|
||||||
|
BrightRed,
|
||||||
|
BrightGreen,
|
||||||
|
BrightYellow,
|
||||||
|
BrightBlue,
|
||||||
|
BrightMagenta,
|
||||||
|
BrightCyan,
|
||||||
|
BrightWhite,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct RGBColor {
|
||||||
pub r: u8,
|
pub r: u8,
|
||||||
pub g: u8,
|
pub g: u8,
|
||||||
pub b: u8,
|
pub b: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl RGBColor {
|
||||||
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
pub fn new(r: u8, g: u8, b: u8) -> Self {
|
||||||
Self { r, g, b }
|
Self { r, g, b }
|
||||||
}
|
}
|
||||||
@ -90,11 +144,11 @@ impl Theme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_THEME: &str = "gruvbox-dark";
|
pub const DEFAULT_THEME: &str = "default";
|
||||||
|
|
||||||
impl Default for Theme {
|
impl Default for Theme {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let theme_content = include_str!("../../../themes/gruvbox-dark.toml");
|
let theme_content = include_str!("../../../themes/default.toml");
|
||||||
toml::from_str(theme_content).unwrap()
|
toml::from_str(theme_content).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,12 +233,46 @@ impl<'de> Deserialize<'de> for Theme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::from_over_into)]
|
#[allow(clippy::from_over_into)]
|
||||||
impl Into<RatatuiColor> for &Color {
|
impl Into<RatatuiColor> for &RGBColor {
|
||||||
fn into(self) -> RatatuiColor {
|
fn into(self) -> RatatuiColor {
|
||||||
RatatuiColor::Rgb(self.r, self.g, self.b)
|
RatatuiColor::Rgb(self.r, self.g, self.b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::from_over_into)]
|
||||||
|
impl Into<RatatuiColor> for &ANSIColor {
|
||||||
|
fn into(self) -> RatatuiColor {
|
||||||
|
match self {
|
||||||
|
ANSIColor::Black => RatatuiColor::Black,
|
||||||
|
ANSIColor::Red => RatatuiColor::Red,
|
||||||
|
ANSIColor::Green => RatatuiColor::Green,
|
||||||
|
ANSIColor::Yellow => RatatuiColor::Yellow,
|
||||||
|
ANSIColor::Blue => RatatuiColor::Blue,
|
||||||
|
ANSIColor::Magenta => RatatuiColor::Magenta,
|
||||||
|
ANSIColor::Cyan => RatatuiColor::Cyan,
|
||||||
|
ANSIColor::White => RatatuiColor::Gray,
|
||||||
|
ANSIColor::BrightBlack => RatatuiColor::DarkGray,
|
||||||
|
ANSIColor::BrightRed => RatatuiColor::LightRed,
|
||||||
|
ANSIColor::BrightGreen => RatatuiColor::LightGreen,
|
||||||
|
ANSIColor::BrightYellow => RatatuiColor::LightYellow,
|
||||||
|
ANSIColor::BrightBlue => RatatuiColor::LightBlue,
|
||||||
|
ANSIColor::BrightMagenta => RatatuiColor::LightMagenta,
|
||||||
|
ANSIColor::BrightCyan => RatatuiColor::LightCyan,
|
||||||
|
ANSIColor::BrightWhite => RatatuiColor::White,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::from_over_into)]
|
||||||
|
impl Into<RatatuiColor> for &Color {
|
||||||
|
fn into(self) -> RatatuiColor {
|
||||||
|
match self {
|
||||||
|
Color::Ansi(ansi) => ansi.into(),
|
||||||
|
Color::Rgb(rgb) => rgb.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::from_over_into)]
|
#[allow(clippy::from_over_into)]
|
||||||
impl Into<Colorscheme> for &Theme {
|
impl Into<Colorscheme> for &Theme {
|
||||||
fn into(self) -> Colorscheme {
|
fn into(self) -> Colorscheme {
|
||||||
@ -265,3 +353,111 @@ impl Into<ModeColorscheme> for &Theme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_theme_deserialization() {
|
||||||
|
let theme_content = r##"
|
||||||
|
background = "#000000"
|
||||||
|
border_fg = "black"
|
||||||
|
text_fg = "white"
|
||||||
|
dimmed_text_fg = "bright-black"
|
||||||
|
input_text_fg = "bright-white"
|
||||||
|
result_count_fg = "bright-white"
|
||||||
|
result_name_fg = "bright-white"
|
||||||
|
result_line_number_fg = "bright-white"
|
||||||
|
result_value_fg = "bright-white"
|
||||||
|
selection_bg = "bright-white"
|
||||||
|
match_fg = "bright-white"
|
||||||
|
preview_title_fg = "bright-white"
|
||||||
|
channel_mode_fg = "bright-white"
|
||||||
|
remote_control_mode_fg = "bright-white"
|
||||||
|
send_to_channel_mode_fg = "bright-white"
|
||||||
|
"##;
|
||||||
|
let theme: Theme = toml::from_str(theme_content).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
theme.background,
|
||||||
|
Some(Color::Rgb(RGBColor::from_str("000000").unwrap()))
|
||||||
|
);
|
||||||
|
assert_eq!(theme.border_fg, Color::Ansi(ANSIColor::Black));
|
||||||
|
assert_eq!(theme.text_fg, Color::Ansi(ANSIColor::White));
|
||||||
|
assert_eq!(theme.dimmed_text_fg, Color::Ansi(ANSIColor::BrightBlack));
|
||||||
|
assert_eq!(theme.input_text_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(theme.result_count_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(theme.result_name_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.result_line_number_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
assert_eq!(theme.result_value_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(theme.selection_bg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(theme.match_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.preview_title_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
assert_eq!(theme.channel_mode_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.remote_control_mode_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
theme.send_to_channel_mode_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_theme_deserialization_no_background() {
|
||||||
|
let theme_content = r##"
|
||||||
|
border_fg = "black"
|
||||||
|
text_fg = "white"
|
||||||
|
dimmed_text_fg = "bright-black"
|
||||||
|
input_text_fg = "bright-white"
|
||||||
|
result_count_fg = "#ffffff"
|
||||||
|
result_name_fg = "bright-white"
|
||||||
|
result_line_number_fg = "#ffffff"
|
||||||
|
result_value_fg = "bright-white"
|
||||||
|
selection_bg = "bright-white"
|
||||||
|
match_fg = "bright-white"
|
||||||
|
preview_title_fg = "bright-white"
|
||||||
|
channel_mode_fg = "bright-white"
|
||||||
|
remote_control_mode_fg = "bright-white"
|
||||||
|
send_to_channel_mode_fg = "bright-white"
|
||||||
|
"##;
|
||||||
|
let theme: Theme = toml::from_str(theme_content).unwrap();
|
||||||
|
assert_eq!(theme.background, None);
|
||||||
|
assert_eq!(theme.border_fg, Color::Ansi(ANSIColor::Black));
|
||||||
|
assert_eq!(theme.text_fg, Color::Ansi(ANSIColor::White));
|
||||||
|
assert_eq!(theme.dimmed_text_fg, Color::Ansi(ANSIColor::BrightBlack));
|
||||||
|
assert_eq!(theme.input_text_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.result_count_fg,
|
||||||
|
Color::Rgb(RGBColor::from_str("ffffff").unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(theme.result_name_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.result_line_number_fg,
|
||||||
|
Color::Rgb(RGBColor::from_str("ffffff").unwrap())
|
||||||
|
);
|
||||||
|
assert_eq!(theme.result_value_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(theme.selection_bg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(theme.match_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.preview_title_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
assert_eq!(theme.channel_mode_fg, Color::Ansi(ANSIColor::BrightWhite));
|
||||||
|
assert_eq!(
|
||||||
|
theme.remote_control_mode_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
theme.send_to_channel_mode_fg,
|
||||||
|
Color::Ansi(ANSIColor::BrightWhite)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ use lazy_static::lazy_static;
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref BUILTIN_THEMES: HashMap<&'static str, &'static str> = {
|
pub static ref BUILTIN_THEMES: HashMap<&'static str, &'static str> = {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
|
m.insert("default", include_str!("../../../../themes/default.toml"));
|
||||||
m.insert(
|
m.insert(
|
||||||
"television",
|
"television",
|
||||||
include_str!("../../../../themes/television.toml"),
|
include_str!("../../../../themes/television.toml"),
|
||||||
|
20
themes/default.toml
Normal file
20
themes/default.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# general
|
||||||
|
# background = 'black'
|
||||||
|
border_fg = 'bright-black'
|
||||||
|
text_fg = 'bright-blue'
|
||||||
|
dimmed_text_fg = 'white'
|
||||||
|
# input
|
||||||
|
input_text_fg = 'bright-red'
|
||||||
|
result_count_fg = 'bright-red'
|
||||||
|
# results
|
||||||
|
result_name_fg = 'bright-blue'
|
||||||
|
result_line_number_fg = 'bright-yellow'
|
||||||
|
result_value_fg = 'white'
|
||||||
|
selection_bg = 'bright-black'
|
||||||
|
match_fg = 'bright-red'
|
||||||
|
# preview
|
||||||
|
preview_title_fg = 'bright-magenta'
|
||||||
|
# modes
|
||||||
|
channel_mode_fg = 'green'
|
||||||
|
remote_control_mode_fg = 'yellow'
|
||||||
|
send_to_channel_mode_fg = 'cyan'
|
Loading…
x
Reference in New Issue
Block a user