new palette enum to accomodate different palette types

This commit is contained in:
kendy-lovely 2026-04-28 00:07:12 +07:00
parent 3bf7a79b4c
commit 50c6a925a8
5 changed files with 214 additions and 41 deletions

View File

@ -8,6 +8,7 @@ use std::iter;
use std::iter::zip;
use std::num::NonZeroU8;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use aho_corasick::AhoCorasick;
use anyhow::{Context as _, Result};
@ -24,7 +25,7 @@ use hyfetch::color_util::{
NeofetchAsciiIndexedColor, PresetIndexedColor, Theme as _, ToAnsiString as _,
};
use hyfetch::distros::Distro;
use hyfetch::models::{build_hex_color_profile, Config, PresetValue};
use hyfetch::models::{Config, Palette, PresetValue, build_hex_color_profile};
#[cfg(feature = "macchina")]
use hyfetch::neofetch_util::macchina_path;
use hyfetch::neofetch_util::{self, add_pkg_path, fastfetch_path, get_distro_ascii, get_distro_name, literal_input, ColorAlignment, NEOFETCH_COLORS_AC, NEOFETCH_COLOR_PATTERNS, TEST_ASCII};
@ -137,8 +138,20 @@ fn main() -> Result<()> {
let args = options.args.as_ref().or(config.args.as_ref());
#[cfg(feature = "macchina")]
let palette_glyph: Option<&String> = options.palette_glyph.as_ref().or(config.palette_glyph.as_ref());
#[cfg(feature = "macchina")]
let palette_type: Option<&String> = options.palette_type.as_ref().or(config.palette_glyph.as_ref());
#[cfg(feature = "macchina")]
let palette: Option<Palette> = if let Some(glyph) = palette_glyph {
if let Some(type_str) = palette_type {
if let Ok(mut parsed_type) = Palette::from_str(type_str) {
parsed_type.get_glyph_mut().push_str(glyph);
Some(parsed_type)
} else { Some(Palette::Full(glyph.to_owned())) }
} else { Some(Palette::Full(glyph.to_owned())) }
} else { None };
#[cfg(not(feature = "macchina"))]
let palette_glyph: Option<&String> = None;
let palette: Option<&String> = None;
fn parse_preset_string(preset_string: &str, config: &Config) -> Result<ColorProfile> {
if preset_string.contains('#') {
@ -229,7 +242,7 @@ fn main() -> Result<()> {
let asc = asc
.to_recolored(&color_align, &color_profile, color_mode, theme)
.context("failed to recolor ascii")?;
neofetch_util::run(asc, backend, args, palette_glyph)?;
neofetch_util::run(asc, backend, args, palette)?;
if options.ask_exit {
@ -1239,44 +1252,54 @@ fn create_config(
//////////////////////////////
// 7.5. If using macchina, ask for custom palette glyph
#[cfg(feature = "macchina")]
let mut palette_glyph: Option<String> = None;
let mut palette: Option<Palette> = Some(Palette::Full("".to_owned()));
#[cfg(feature = "macchina")]
if backend == Backend::Macchina {
use hyfetch::formatc;
let mut current_title = "Create a glyph for macchina (leave empty for None):";
clear_screen(Some(&title), color_mode, debug_mode)
.context("failed to clear screen")?;
print_title_prompt(option_counter, "Create a glyph for macchina (leave empty for None):");
print_title_prompt(option_counter, current_title);
let color_palette: [&str; 8] = match theme {
TerminalTheme::Dark => ["&0","&4","&2","&6","&1","&5","&3","&8"],
TerminalTheme::Light => ["&8", "&c", "&a", "&e", "&9", "&d", "&b", "&f"]
};
const DARK_COLORS: [&str; 8] = ["&0","&4","&2","&6","&1","&5","&3","&8"];
const LIGHT_COLORS: [&str; 8] = ["&8","&c","&a","&e","&9","&d","&b","&f"];
let print_palette = |glyph: &str, palette_list: [&str; 8]| -> Result<()> {
clear_screen(Some(&title), color_mode, debug_mode)
.context("failed to clear screen")?;
print_title_prompt(option_counter, "Create a glyph for macchina (leave empty for None):");
let palette_for_print: String = palette_list
let to_palette = |glyph: &str, colors: [&str; 8]| -> String {
colors
.into_iter()
.map(|s| s.to_owned() + glyph + "&r" )
.collect();
.collect()
};
printc!("Preview:\n{}", palette_for_print);
print!("> {glyph}");
let print_palette = |palette: &mut Palette, current_title: &str| -> Result<()> {
clear_screen(Some(&title), color_mode, debug_mode)
.context("failed to clear screen")?;
print_title_prompt(option_counter, current_title);
match palette {
Palette::Full(glyph) =>
printc!("Preview:\n{}\n{}\n", to_palette(glyph, DARK_COLORS),
to_palette(glyph, LIGHT_COLORS)),
Palette::Light(glyph) =>
printc!("Preview:\n{}\n\n", to_palette(glyph, LIGHT_COLORS)),
Palette::Dark(glyph) =>
printc!("Preview:\n{}\n\n", to_palette(glyph, DARK_COLORS))
}
print!("> {}", palette.get_glyph());
io::stdout().flush().context("failed to flush preset prompt")?;
Ok(())
};
let mut raw_mode = RawModeGuard::new().context("failed to initialize raw input mode")?;
let mut entered_glyph = String::new();
loop {
raw_mode
.disable()
.context("failed to disable raw mode for rendering")?;
print_palette(&entered_glyph, color_palette).context("failed to clear screen during glyph input")?;
print_palette(palette.as_mut().unwrap(), current_title).context("failed to print palette during glyph input")?;
raw_mode
.enable()
@ -1294,10 +1317,18 @@ fn create_config(
break;
},
KeyCode::Backspace => {
entered_glyph.pop();
palette
.as_mut()
.unwrap()
.get_glyph_mut()
.pop();
},
KeyCode::Esc => {
entered_glyph.clear();
palette
.as_mut()
.unwrap()
.get_glyph_mut()
.clear();
},
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
raw_mode
@ -1310,27 +1341,97 @@ fn create_config(
if !key.modifiers.contains(KeyModifiers::CONTROL)
&& !key.modifiers.contains(KeyModifiers::ALT) =>
{
entered_glyph.push(c);
palette
.as_mut()
.unwrap()
.get_glyph_mut()
.push(c);
},
_ => {},
}
}
raw_mode
.disable()
.context("failed to disable raw mode after preset selection")?;
.context("failed to disable raw mode after entering glyph")?;
drop(raw_mode);
if palette.as_ref().unwrap().get_glyph().is_empty() {
palette = None;
}
palette_glyph = if !entered_glyph.is_empty() {
Some(entered_glyph)
} else { None };
if let Some(palette) = palette.as_mut() {
current_title = "Pick your palette type!";
let mut raw_mode = RawModeGuard::new().context("failed to initialize raw input mode")?;
let mut select_display: String;
loop {
raw_mode
.disable()
.context("failed to disable raw mode for rendering")?;
print_palette(palette, current_title).context("failed to clear screen during glyph input")?;
select_display = match palette {
Palette::Full(_) => formatc!("&l&o&nFull&r, Light, Dark"),
Palette::Light(_) => formatc!("Full, &l&o&nLight&r, Dark"),
Palette::Dark(_) => formatc!("Full, Light, &l&o&nDark")
};
println!("\n> {}", select_display);
raw_mode
.enable()
.context("failed to enable raw mode for key input")?;
let event = event::read().context("failed to read keyboard event")?;
let Event::Key(key) = event else {
continue;
};
if !matches!(key.kind, KeyEventKind::Press | KeyEventKind::Repeat) {
continue;
}
match key.code {
KeyCode::Enter => break,
KeyCode::Left => palette.shift_type(false),
KeyCode::Right => palette.shift_type(true),
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
raw_mode
.disable()
.context("failed to disable raw mode before interrupting")?;
println!();
return Err(anyhow::anyhow!("interrupted by user"));
},
_ => {}
}
}
};
let preview: &str = match palette.as_ref() {
Some(p) => match p {
Palette::Full(glyph) =>
&formatc!("\n{}\n{}", to_palette(glyph, DARK_COLORS),
to_palette(glyph, LIGHT_COLORS)),
Palette::Light(glyph) =>
&formatc!("\n{}", to_palette(glyph, LIGHT_COLORS)),
Palette::Dark(glyph) =>
&formatc!("\n{}", to_palette(glyph, DARK_COLORS))
},
None => "None"
};
update_title(
&mut title,
&mut option_counter,
"Selected glyph",
&palette_glyph.as_ref().unwrap_or(&String::from("None")),
"Customized palette",
preview
);
}
let palette_glyph: Option<String> = if let Some(palette) = palette.as_ref() {
Some(palette.get_glyph().to_owned())
} else { None };
let palette_type: Option<String> = if let Some(palette) = palette.as_ref() {
Some(palette.to_string())
} else { None };
//////////////////////////////
// 8. Custom ASCII file
let mut custom_ascii_path: Option<String> = None;
@ -1392,8 +1493,8 @@ fn create_config(
pride_month_disable: false,
custom_ascii_path,
custom_presets: None,
#[cfg(feature = "macchina")]
palette_glyph
palette_glyph,
palette_type
};
debug!(?config, "created config");

View File

@ -33,7 +33,9 @@ pub struct Options {
pub ask_exit: bool,
pub auto_detect_light_dark: Option<bool>,
#[cfg(feature = "macchina")]
pub palette_glyph: Option<String>
pub palette_glyph: Option<String>,
#[cfg(feature = "macchina")]
pub palette_type: Option<String>
}
pub fn options() -> OptionParser<Options> {
@ -160,6 +162,12 @@ BACKEND={{{backends}}}",
.help("Sets the glyph to be used for the macchina backend")
.argument("STR")
.optional();
#[cfg(feature = "macchina")]
let palette_type = long("palette-type")
.help("Sets the type of palette to be used for the macchina backend")
.argument("full,light,dark")
.optional();
#[cfg(feature = "macchina")]
#[cfg(feature = "macchina")]
return construct!(Options {
@ -180,7 +188,8 @@ BACKEND={{{backends}}}",
test_print,
ask_exit,
auto_detect_light_dark,
palette_glyph
palette_glyph,
palette_type
})
.to_options()
.header(

View File

@ -410,6 +410,13 @@ macro_rules! printc {
};
}
#[macro_export]
macro_rules! formatc {
($($arg:tt)*) => {
format!("{}", color(format!("{}&r", format!($($arg)*)), AnsiMode::Rgb).expect("failed to color message"));
};
}
/// Prints with color.
pub fn printc<S>(msg: S, mode: AnsiMode) -> Result<()>
where

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use anyhow::{Context as _, Result};
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, EnumString};
use crate::color_profile::ColorProfile;
use crate::color_util::Lightness;
@ -25,7 +26,9 @@ pub struct Config {
pub custom_ascii_path: Option<String>,
pub custom_presets: Option<HashMap<String, Vec<String>>>,
#[cfg(feature = "macchina")]
pub palette_glyph: Option<String>
pub palette_glyph: Option<String>,
#[cfg(feature = "macchina")]
pub palette_type: Option<String>
}
impl Config {
@ -188,3 +191,52 @@ impl PresetValue {
}
}
}
// handling macchina palettes
#[derive(Clone, Debug, Display, EnumString, Deserialize, Serialize, AsRefStr)]
pub enum Palette {
#[strum(to_string = "Full", ascii_case_insensitive)]
Full(String),
#[strum(to_string = "Light", ascii_case_insensitive)]
Light(String),
#[strum(to_string = "Dark", ascii_case_insensitive)]
Dark(String)
}
impl Palette {
pub fn get_glyph(&self) -> &String {
match self {
Palette::Full(s) => s,
Palette::Light(s) => s,
Palette::Dark(s) => s
}
}
pub fn get_glyph_mut(&mut self) -> &mut String {
match self {
Palette::Full(s) => s,
Palette::Light(s) => s,
Palette::Dark(s) => s
}
}
pub fn shift_type(&mut self, go_right: bool) {
*self = if go_right {
match self {
Palette::Full(s) => Palette::Light(s.to_owned()),
Palette::Light(s) => Palette::Dark(s.to_owned()),
Palette::Dark(s) => Palette::Full(s.to_owned())
}
} else {
match self {
Palette::Full(s) => Palette::Dark(s.to_owned()),
Palette::Light(s) => Palette::Full(s.to_owned()),
Palette::Dark(s) => Palette::Light(s.to_owned())
}
}
}
}
#[test]
fn test() {
println!("{:?}", Palette::Full("".to_owned()))
}

View File

@ -13,6 +13,9 @@ use indexmap::IndexMap;
use itertools::Itertools as _;
#[cfg(windows)]
use anyhow::anyhow;
#[cfg(feature = "macchina")]
use crate::models::Palette;
#[cfg(feature = "macchina")]
#[cfg(windows)]
use crate::utils::find_file;
#[cfg(windows)]
@ -275,14 +278,14 @@ where
}
#[tracing::instrument(level = "debug", skip(asc), fields(asc.w = asc.w, asc.h = asc.h))]
pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec<String>>, palette_glyph: Option<&String>) -> Result<()> {
pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec<String>>, palette: Option<Palette>) -> Result<()> {
let asc = asc.lines.join("\n");
match backend {
Backend::Neofetch => run_neofetch(asc, args).context("failed to run neofetch")?,
Backend::Fastfetch => run_fastfetch(asc, args).context("failed to run fastfetch")?,
#[cfg(feature = "macchina")]
Backend::Macchina => run_macchina(asc, args, palette_glyph).context("failed to run macchina")?,
Backend::Macchina => run_macchina(asc, args, palette).context("failed to run macchina")?,
}
Ok(())
@ -666,7 +669,7 @@ fn run_fastfetch(asc: String, args: Option<&Vec<String>>) -> Result<()> {
/// Runs macchina with custom ascii art.
#[cfg(feature = "macchina")]
#[tracing::instrument(level = "debug", skip(asc))]
fn run_macchina(asc: String, args: Option<&Vec<String>>, palette_glyph: Option<&String>) -> Result<()> {
fn run_macchina(asc: String, args: Option<&Vec<String>>, palette: Option<Palette>) -> Result<()> {
// Write ascii art to temp file
let asc_file_path = {
let mut temp_file = tempfile::Builder::new()
@ -697,9 +700,10 @@ fn run_macchina(asc: String, args: Option<&Vec<String>>, palette_glyph: Option<&
"path",
&*asc_file_path.to_string_lossy(),
)]));
if let Some(palette_glyph) = palette_glyph {
if let Some(p) = palette {
doc["palette"] = Item::Table(Table::from_iter([
("glyph", value(palette_glyph)),
("type", value(p.to_string())),
("glyph", value(p.get_glyph())),
("visible", value(true))
]))
}