[PR] Auto detect light/dark mode, changing the theme accordingly (#380)

* disable clippy lint

* fmt

* move detect bg into a function

* auto detect light/dark opt

* auto detect dark cli opt

* optional light-dark

* cli opt

* nicer looking output

* [-] Revert code style changes

* [-] Revert code style changes

* [-] Revert code style changes

* [-] Revert

* make the default theme dark

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
This commit is contained in:
Alice 2025-02-17 04:58:45 +07:00 committed by GitHub
parent 8a536c30f0
commit 0cc6ddbca8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 51 additions and 23 deletions

View file

@ -87,7 +87,16 @@ fn main() -> Result<()> {
};
let color_mode = options.mode.unwrap_or(config.mode);
let theme = config.light_dark;
let auto_detect_light_dark = options
.auto_detect_light_dark
.unwrap_or_else(|| config.auto_detect_light_dark.unwrap_or(false));
let theme = if auto_detect_light_dark {
let res = det_bg();
res?.map(|bg| bg.theme())
.unwrap_or(config.light_dark.unwrap_or_default())
} else {
config.light_dark.unwrap_or_default()
};
// Check if it's June (pride month)
let now =
@ -131,7 +140,12 @@ fn main() -> Result<()> {
} else if let Some(lightness) = options.lightness {
color_profile.with_lightness(AssignLightness::Replace(lightness))
} else {
color_profile.with_lightness_adaptive(config.lightness(), theme)
color_profile.with_lightness_adaptive(
config
.lightness
.unwrap_or_else(|| Config::default_lightness(theme)),
theme,
)
};
debug!(?color_profile, "lightened color profile");
@ -187,6 +201,22 @@ fn load_config(path: &PathBuf) -> Result<Option<Config>> {
Ok(Some(config))
}
fn det_bg() -> Result<Option<Srgb<u8>>, terminal_colorsaurus::Error> {
if !io::stdout().is_terminal() {
return Ok(None);
}
background_color(QueryOptions::default())
.map(|terminal_colorsaurus::Color { r, g, b }| Some(Srgb::new(r, g, b).into_format()))
.or_else(|err| {
if matches!(err, terminal_colorsaurus::Error::UnsupportedTerminal) {
Ok(None)
} else {
Err(err)
}
})
}
/// Creates config interactively.
///
/// The config is automatically stored to file.
@ -197,21 +227,10 @@ fn create_config(
backend: Backend,
debug_mode: bool,
) -> Result<Config> {
// Detect terminal environment (doesn't work for all terminal emulators,
// especially on Windows)
let det_bg = if io::stdout().is_terminal() {
match background_color(QueryOptions::default()) {
Ok(bg) => Some(Srgb::<u16>::new(bg.r, bg.g, bg.b).into_format::<u8>()),
Err(terminal_colorsaurus::Error::UnsupportedTerminal) => None,
Err(err) => {
return Err(err).context("failed to get terminal background color");
},
}
} else {
None
};
let det_bg = det_bg()?;
debug!(?det_bg, "detected background color");
let det_ansi = supports_color::on(supports_color::Stream::Stdout).map(|color_level| {
#[allow(clippy::if_same_then_else)]
if color_level.has_16m {
AnsiMode::Rgb
} else if color_level.has_256 {
@ -230,7 +249,7 @@ fn create_config(
let asc = get_distro_ascii(distro, backend).context("failed to get distro ascii")?;
let asc = asc.to_normalized().context("failed to normalize ascii")?;
let theme = det_bg.map(|bg| bg.theme()).unwrap_or(TerminalTheme::Light);
let theme = det_bg.map(|bg| bg.theme()).unwrap_or_default();
let color_mode = det_ansi.unwrap_or(AnsiMode::Ansi256);
let mut title = format!(
"Welcome to {logo} Let's set up some colors first.",
@ -1002,7 +1021,8 @@ fn create_config(
let config = Config {
preset,
mode: color_mode,
light_dark: theme,
light_dark: Some(theme),
auto_detect_light_dark: Some(det_bg.is_some()),
lightness: Some(lightness),
color_align,
backend,

View file

@ -31,6 +31,7 @@ pub struct Options {
pub print_font_logo: bool,
pub test_print: bool,
pub ask_exit: bool,
pub auto_detect_light_dark: Option<bool>,
}
pub fn options() -> OptionParser<Options> {
@ -170,6 +171,10 @@ BACKEND={{{backends}}}",
.help("Ask for input before exiting")
.switch()
.hide();
let auto_detect_light_dark = long("auto-detect-light-dark")
.help("Enables hyfetch to detect light/dark terminal background in runtime")
.argument("BOOL")
.optional();
construct!(Options {
config,
@ -188,6 +193,7 @@ BACKEND={{{backends}}}",
// hidden
test_print,
ask_exit,
auto_detect_light_dark,
})
.to_options()
.header(

View file

@ -9,7 +9,8 @@ use crate::types::{AnsiMode, Backend, TerminalTheme};
pub struct Config {
pub preset: Preset,
pub mode: AnsiMode,
pub light_dark: TerminalTheme,
pub auto_detect_light_dark: Option<bool>,
pub light_dark: Option<TerminalTheme>,
pub lightness: Option<Lightness>,
pub color_align: ColorAlignment,
pub backend: Backend,
@ -31,11 +32,6 @@ impl Config {
},
}
}
pub fn lightness(&self) -> Lightness {
self.lightness
.unwrap_or_else(|| Self::default_lightness(self.light_dark))
}
}
mod args_serde {

View file

@ -36,6 +36,12 @@ pub enum TerminalTheme {
Dark,
}
impl Default for TerminalTheme {
fn default() -> Self {
Self::Dark
}
}
#[derive(
Copy,
Clone,