Compare commits

..

29 Commits

Author SHA1 Message Date
azalea 15ca855a04 [O] Shorten 2025-10-02 02:11:58 +08:00
azalea 666d2dc90a Update hyfetch.rs 2025-10-02 01:33:25 +08:00
azalea 34583294c6 [O] Make code more readable 2025-10-02 01:25:07 +08:00
azalea 03615ab4ee [O] Make code more readable 2025-10-02 01:06:39 +08:00
azalea c722c73e79 [O] shorten code 2025-10-02 00:44:05 +08:00
azalea 8f5199974b [F] Fix windows build #439 2025-10-02 00:43:25 +08:00
azalea 8168877fb1 [U] Update crates 2025-10-02 00:40:05 +08:00
azalea 4e20b18d45 [U] Upgrade deps 2025-10-01 23:58:01 +08:00
thea 5dc1709f58 [+] Allow passing hex colors as preset (#435) 2025-10-01 08:13:09 -07:00
Un1q32 fb1e35172e Support old Apple TV models (#438) 2025-10-01 08:12:37 -07:00
ObsoleteDev fc9292be3f 🌈 Support custom ASCII art file path (#429)
* Feature: Add custom ascii file saving to python version of hyfetch

* Feature: Add custom ascii file saving to rust version of hyfetch

* [-] Remove test ascii

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-10-01 08:12:05 -07:00
ObsoleteDev 3f41cb40e2 [+] Add fluidflux flags (#437)
* [+] Add fluidflux flags to py

* [+] Add fluidflux flags rust
2025-09-18 21:48:54 -07:00
ObsoleteDev 729024a45f [+] libragender flags (#433)
* [+] libragender flags py

* [+] libragender flags rust
2025-09-13 02:21:01 +09:00
ObsoleteDev ef1407d00e [F] Only mark pride month easter egg as displayed when its june (rust) (#430)
fix(rust): Only mark pride month easter egg as displayed when its june
2025-09-10 07:49:32 -07:00
Thundertides 075fc467d2 Temporary fix to GH-399 (#428) 2025-09-07 09:59:02 -07:00
azalea a463f4234c [U] Release 2.0.2 2025-09-04 07:12:01 -04:00
azalea beb04101db [F] Fix script 2025-09-04 07:09:56 -04:00
azalea bf60e4265b [F] Fix script 2025-09-04 07:05:40 -04:00
azalea 42421bc57d [+] Changelog 2025-09-04 06:52:58 -04:00
azalea f64ee7b7e9 [O] Better warning message #419 2025-09-04 06:46:20 -04:00
azalea 1fa29cf831 [+] Adipophilia flag (#424) 2025-09-04 06:24:10 -04:00
azalea 4861dd5d4a [+] Nullflux flag #397 2025-09-04 06:11:55 -04:00
azalea 5614b723fd [+] Hypergender flags
Fixes hykilpikonna/hyfetch#422
2025-09-04 06:04:37 -04:00
azalea 5e1e13b091 [M] Move flag order 2025-09-04 05:52:17 -04:00
azalea 79ddb3ca92 [+] Fictosexual flag (#394) 2025-09-04 05:41:49 -04:00
azalea 684929edea [U] gitignore 2025-09-04 05:41:49 -04:00
Emi f02ec8c4ba Fix nixos_small logo (#421)
* [F] Fix nixos_small logo

* [F] Fix nixos_small logo missing line break

* [?] i was trying to fix a difference between the python version and the rust version what the hell dude
2025-08-27 15:10:59 +09:00
azalea 5fd4ed9b0c [F] Fix #420 fastfetch custom config 2025-08-25 02:20:24 -04:00
a catgirl 003c295084 [F] fix: custom *fetch configurations breaking hyfetch (#420)
if the backend is fastfetch, hyfetch calls
`/bin/fastfetch --logo none -s OS --disable-linewrap`
which for me returns:
`  Artix Linux x86_64`
... and that breaks hyfetch

instead, get the information from the source (/etc/os-release), which im
pretty sure most fetch backends read from anyway

this won't work on *BSD though, because those lack
/etc/os-release, so unless calling `uname -s` is alright, BSDs would
still have the problem

related pull: https://github.com/hykilpikonna/hyfetch/issues/418
2025-08-23 17:34:57 -07:00
25 changed files with 1055 additions and 808 deletions
+12 -1
View File
@@ -120,4 +120,15 @@ webhook-log
start_moderation.sh
gh_moderator.toml
moderator-data
hyfetch/git
hyfetch/git
build
dist
target
.vscode
.idea
*.iml
*.egg-info
__pycache__
*.pyc
*.pyo
*.pyd
Generated
+324 -243
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "2.0.1"
version = "2.0.2"
authors = ["Azalea Gui <azalea@hydev.org>"]
edition = "2021"
rust-version = "1.75.0"
@@ -17,14 +17,14 @@ ansi_colours = { version = "1.2.2", default-features = false }
anstream = { version = "0.6.14", default-features = false }
anyhow = { version = "1.0.86", default-features = false }
bpaf = { version = "0.9.12", default-features = false }
crossterm = { version = "0.27.0", default-features = false }
crossterm = { version = "0.29.0", default-features = false }
deranged = { version = "0.3.11", default-features = false }
directories = { version = "5.0.1", default-features = false }
directories = { version = "6.0.0", default-features = false }
enable-ansi-support = { version = "0.2.1", default-features = false }
enterpolation = { version = "0.2.1", default-features = false }
fastrand = { version = "2.1.0", default-features = false }
indexmap = { version = "2.2.6", default-features = false }
itertools = { version = "0.13.0", default-features = false }
itertools = { version = "0.14.0", default-features = false }
normpath = { version = "1.2.0", default-features = false }
palette = { version = "0.7.6", default-features = false }
regex = { version = "1.10.5", default-features = false }
@@ -33,14 +33,14 @@ serde = { version = "1.0.203", default-features = false }
serde_json = { version = "1.0.118", default-features = false }
serde_path_to_error = { version = "0.1.16", default-features = false }
shell-words = { version = "1.1.0", default-features = false }
strum = { version = "0.26.3", default-features = false }
strum = { version = "0.27.2", default-features = false }
supports-color = { version = "3.0.0", default-features = false }
tempfile = { version = "3.10.1", default-features = false }
terminal-colorsaurus = { version = "0.4.3", default-features = false }
terminal_size = { version = "0.3.0", default-features = false }
terminal-colorsaurus = { version = "1.0.0", default-features = false }
terminal_size = { version = "0.4.3", default-features = false }
thiserror = { version = "1.0.61", default-features = false }
time = { version = "0.3.36", default-features = false }
toml_edit = { version = "0.22.16", default-features = false }
toml_edit = { version = "0.23.6", default-features = false }
tracing = { version = "0.1.40", default-features = false }
tracing-subscriber = { version = "0.3.18", default-features = false }
unicode-normalization = { version = "0.1.23", default-features = false }
+11
View File
@@ -129,6 +129,17 @@ cargo install --git https://github.com/hykilpikonna/hyfetch
<!-- CHANGELOG STARTS HERE --->
### 2.0.2
This is a small patch release that adds more flags and fixes some bugs from the recent Rust rewrite.
* 🏳️‍🌈 **New Flags**: Added new pride flags for Nullflux ([#397](https://github.com/hykilpikonna/hyfetch/pull/397)), Hypergender ([#422](https://github.com/hykilpikonna/hyfetch/pull/422)), Fictosexual ([#394](https://github.com/hykilpikonna/hyfetch/pull/394)), and Adipophilia ([#424](https://github.com/hykilpikonna/hyfetch/pull/424)).
* 🐛 **Bug Fixes**:
* Fixed a critical bug where custom `fastfetch` configurations could break hyfetch ([#420](https://github.com/hykilpikonna/hyfetch/pull/420)).
* Corrected the `nixos_small` ASCII logo to display properly ([#421](https://github.com/hykilpikonna/hyfetch/pull/421)).
* 🔧 **Improvements**:
* Improved a warning message for better clarity ([#419](https://github.com/hykilpikonna/hyfetch/issues/419)).
### 2.0.1
(changelog is generated by Gemini from commit history)
+2 -1
View File
@@ -34,7 +34,7 @@ fn main() {
let dir = PathBuf::from(env::var_os("CARGO_WORKSPACE_DIR").unwrap_or_else(|| env::var_os("CARGO_MANIFEST_DIR").unwrap()));
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
for file in &["neofetch", "hyfetch"] {
for file in &["neofetch", "hyfetch/data"] {
let src = anything_that_exist(&[
&dir.join(file),
&dir.join("../../").join(file),
@@ -45,6 +45,7 @@ fn main() {
// Copy either file or directory
if src.is_dir() {
let opt = CopyOptions { overwrite: true, copy_inside: true, ..CopyOptions::default() };
println!("copying {} to {}", src.display(), dst.display());
fs_extra::dir::copy(&src, &dst, &opt).expect("Failed to copy directory to OUT_DIR");
}
else { fs::copy(&src, &dst).expect("Failed to copy file to OUT_DIR"); }
+2 -2
View File
@@ -61,8 +61,8 @@ impl RawAsciiArt {
Ok(NormalizedAsciiArt {
lines,
w,
h,
w: w.try_into().context("width does not fit in u8")?,
h: h.try_into().context("height does not fit in u8")?,
fg: self.fg.clone(),
})
}
+150 -180
View File
@@ -2,7 +2,8 @@ use std::borrow::Cow;
use std::cmp;
use std::fmt::Write as _;
use std::fs::{self, File};
use std::io::{self, IsTerminal as _, Read as _, Write as _};
use std::io::{self, IsTerminal as _, Read as _};
use std::iter;
use std::iter::zip;
use std::num::NonZeroU8;
use std::path::{Path, PathBuf};
@@ -24,8 +25,8 @@ use hyfetch::models::Config;
#[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};
use hyfetch::presets::{AssignLightness, Preset};
use hyfetch::pride_month;
use hyfetch::presets::{AssignLightness, ColorProfile, Preset};
use hyfetch::{pride_month, printc};
use hyfetch::types::{AnsiMode, Backend, TerminalTheme};
use hyfetch::utils::{get_cache_path, input};
use hyfetch::font_logo::get_font_logo;
@@ -64,15 +65,12 @@ fn main() -> Result<()> {
});
if options.test_print {
let asc = get_distro_ascii(distro, backend).context("failed to get distro ascii")?;
writeln!(io::stdout(), "{asc}", asc = asc.asc)
.context("failed to write ascii to stdout")?;
println!("{asc}", asc = get_distro_ascii(distro, backend)?.asc);
return Ok(());
}
if options.print_font_logo {
let logo = get_font_logo(backend).context("failed to get font logo")?;
writeln!(io::stdout(), "{}", logo).context("failed to write logo to stdout")?;
println!("{}", get_font_logo(backend)?);
return Ok(());
}
@@ -110,14 +108,9 @@ fn main() -> Result<()> {
if show_pride_month && !config.pride_month_disable {
pride_month::start_animation(color_mode).context("failed to draw pride month animation")?;
writeln!(
io::stdout(),
"\nHappy pride month!\n(You can always view the animation again with `hyfetch \
--june`)\n"
)
.context("failed to write message to stdout")?;
println!("\nHappy pride month!\n(You can always view the animation again with `hyfetch --june`)\n");
if !june_path.is_file() {
if !june_path.is_file() && !options.june {
File::create(&june_path)
.with_context(|| format!("failed to create file {june_path:?}"))?;
}
@@ -129,9 +122,43 @@ fn main() -> Result<()> {
let backend = options.backend.unwrap_or(config.backend);
let args = options.args.as_ref().or(config.args.as_ref());
fn parse_preset_string(preset_string: &str) -> Result<ColorProfile> {
if preset_string.contains('#') {
let colors: Vec<&str> = preset_string.split(',').map(|s| s.trim()).collect();
for color in &colors {
if !color.starts_with('#') ||
(color.len() != 4 && color.len() != 7) ||
!color[1..].chars().all(|c| c.is_ascii_hexdigit()) {
return Err(anyhow::anyhow!("invalid hex color: {}", color));
}
}
ColorProfile::from_hex_colors(colors)
.context("failed to create color profile from hex")
} else if preset_string == "random" {
let mut rng = fastrand::Rng::new();
let preset = *rng
.choice(<Preset as VariantArray>::VARIANTS)
.expect("preset iterator should not be empty");
Ok(preset.color_profile())
} else {
use std::str::FromStr;
let preset = Preset::from_str(preset_string)
.with_context(|| {
format!(
"PRESET should be comma-separated hex colors or one of {{{presets}}}",
presets = <Preset as VariantNames>::VARIANTS
.iter()
.chain(iter::once(&"random"))
.join(",")
)
})?;
Ok(preset.color_profile())
}
}
// Get preset
let preset = options.preset.unwrap_or(config.preset);
let color_profile = preset.color_profile();
let preset_string = options.preset.as_deref().unwrap_or(&config.preset);
let color_profile = parse_preset_string(preset_string)?;
debug!(?color_profile, "color profile");
// Lighten
@@ -149,7 +176,14 @@ fn main() -> Result<()> {
};
debug!(?color_profile, "lightened color profile");
let asc = if let Some(path) = options.ascii_file {
let asc = if let Some(path_str) = config.custom_ascii_path {
let path = PathBuf::from(path_str);
RawAsciiArt {
asc: fs::read_to_string(&path)
.with_context(|| format!("failed to read ascii from {path:?}"))?,
fg: Vec::new(),
}
} else if let Some(path) = options.ascii_file {
RawAsciiArt {
asc: fs::read_to_string(&path)
.with_context(|| format!("failed to read ascii from {path:?}"))?,
@@ -207,9 +241,9 @@ fn det_bg() -> Result<Option<Srgb<u8>>, terminal_colorsaurus::Error> {
}
background_color(QueryOptions::default())
.map(|terminal_colorsaurus::Color { r, g, b }| Some(Srgb::new(r, g, b).into_format()))
.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) {
if matches!(err, terminal_colorsaurus::Error::UnsupportedTerminal(_)) {
Ok(None)
} else {
Err(err)
@@ -236,10 +270,6 @@ fn create_config(
} else if color_level.has_256 {
AnsiMode::Ansi256
} else if color_level.has_basic {
// unimplemented!(
// "{mode} color mode not supported",
// mode = AnsiMode::Ansi16.as_ref()
// );
AnsiMode::Ansi256
} else {
unreachable!();
@@ -279,13 +309,8 @@ fn create_config(
.expect("`option_counter` should not overflow `u8`");
}
fn print_title_prompt(
option_counter: NonZeroU8,
prompt: &str,
color_mode: AnsiMode,
) -> Result<()> {
printc(format!("&a{option_counter}. {prompt}"), color_mode)
.context("failed to print prompt")
fn print_title_prompt(option_counter: NonZeroU8, prompt: &str) {
printc!("&a{option_counter}. {prompt}");
}
//////////////////////////////
@@ -295,14 +320,8 @@ fn create_config(
let (Width(term_w), Height(term_h)) = terminal_size().context("failed to get terminal size")?;
let (term_w_min, term_h_min) = ((asc.w as u32 * 2 + 4).clamp(0, u16::MAX.into()) as u16, 30);
if term_w < term_w_min || term_h < term_h_min {
printc(
format!(
"&cWarning: Your terminal is too small ({term_w} * {term_h}).\nPlease resize \
it to at least ({term_w_min} * {term_h_min}) for better experience."
),
color_mode,
)
.context("failed to print message")?;
printc!("&cWarning: Your terminal is too small ({term_w} * {term_h}).\n\
Please resize it to at least ({term_w_min} * {term_h_min}) for better experience.");
input(Some("Press enter to continue...")).context("failed to read input")?;
}
}
@@ -347,18 +366,14 @@ fn create_config(
(t - a) * ((d - c) / (b - a)) + c
}
{
let label = format!(
"{label:^term_w$}",
label = "8bit Color Testing",
term_w = usize::from(term_w)
);
let print_color_testing = |label: &str, mode: AnsiMode| {
let label = format!("{label:^term_w$}", term_w = usize::from(term_w));
let line = zip(gradient.iter(), label.chars()).fold(
String::new(),
|mut s, (&rgb_f32_color, t)| {
let rgb_u8_color = Srgb::<u8>::from_linear(rgb_f32_color);
let back = rgb_u8_color
.to_ansi_string(AnsiMode::Ansi256, ForegroundBackground::Background);
.to_ansi_string(mode, ForegroundBackground::Background);
let fore = rgb_u8_color
.contrast_grayscale()
.to_ansi_string(AnsiMode::Ansi256, ForegroundBackground::Foreground);
@@ -366,42 +381,15 @@ fn create_config(
s
},
);
printc(line, AnsiMode::Ansi256).context("failed to print 8-bit color test line")?;
}
{
let label = format!(
"{label:^term_w$}",
label = "RGB Color Testing",
term_w = usize::from(term_w)
);
let line = zip(gradient.iter(), label.chars()).fold(
String::new(),
|mut s, (&rgb_f32_color, t)| {
let rgb_u8_color = Srgb::<u8>::from_linear(rgb_f32_color);
let back = rgb_u8_color
.to_ansi_string(AnsiMode::Rgb, ForegroundBackground::Background);
let fore = rgb_u8_color
.contrast_grayscale()
.to_ansi_string(AnsiMode::Ansi256, ForegroundBackground::Foreground);
write!(s, "{back}{fore}{t}").unwrap();
s
},
);
printc(line, AnsiMode::Rgb).context("failed to print RGB color test line")?;
}
printc!("{line}");
};
writeln!(io::stdout()).context("failed to write to stdout")?;
print_title_prompt(
option_counter,
"Which &bcolor system &ado you want to use?",
color_mode,
)
.context("failed to print title prompt")?;
writeln!(
io::stdout(),
"(If you can't see colors under \"RGB Color Testing\", please choose 8bit)\n"
)
.context("failed to write message to stdout")?;
print_color_testing("8bit Color Testing", AnsiMode::Ansi256);
print_color_testing("RGB Color Testing", AnsiMode::Rgb);
println!();
print_title_prompt(option_counter, "Which &bcolor system &ado you want to use?");
println!("(If you can't see colors under \"RGB Color Testing\", please choose 8bit)\n");
let choice = literal_input(
"Your choice?",
@@ -434,20 +422,14 @@ fn create_config(
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt(
option_counter,
"Is your terminal in &blight mode&~ or &4dark mode&~?",
color_mode,
)
.context("failed to print title prompt")?;
print_title_prompt(option_counter, "Is your terminal in &blight mode&~ or &4dark mode&~?");
let choice = literal_input(
"",
TerminalTheme::VARIANTS,
TerminalTheme::Dark.as_ref(),
true,
color_mode,
)
.context("failed to ask for choice input")?;
)?;
Ok((
choice.parse().expect("selected theme should be valid"),
"Selected background color",
@@ -518,18 +500,12 @@ fn create_config(
let print_flag_page = |page, page_num: u8| -> Result<()> {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt(option_counter, "Let's choose a flag!", color_mode)
.context("failed to print title prompt")?;
writeln!(
io::stdout(),
"Available flag presets:\nPage: {page_num} of {num_pages}\n",
page_num = page_num.checked_add(1).unwrap()
)
.context("failed to write header to stdout")?;
print_title_prompt(option_counter, "Let's choose a flag!");
println!("Available flag presets:\nPage: {page_num} of {num_pages}\n", page_num = page_num + 1);
for &row in page {
print_flag_row(row, color_mode).context("failed to print flag row")?;
}
writeln!(io::stdout()).context("failed to write to stdout")?;
println!();
Ok(())
};
@@ -541,7 +517,7 @@ fn create_config(
}
printc(line.join(" "), color_mode).context("failed to print line")?;
}
writeln!(io::stdout()).context("failed to write to stdout")?;
println!();
Ok(())
}
@@ -566,16 +542,9 @@ fn create_config(
let mut opts: Vec<&str> = <Preset as VariantNames>::VARIANTS.into();
opts.extend(["next", "n", "prev", "p"]);
writeln!(
io::stdout(),
"Enter '[n]ext' to go to the next page and '[p]rev' to go to the previous page."
)
.context("failed to write message to stdout")?;
println!("Enter '[n]ext' to go to the next page and '[p]rev' to go to the previous page.");
let selection = literal_input(
format!(
"Which {preset} do you want to use? ",
preset = preset_default_colored
),
format!("Which {preset_default_colored} do you want to use? "),
&opts[..],
Preset::Rainbow.as_ref(),
false,
@@ -624,22 +593,15 @@ fn create_config(
let select_lightness = || -> Result<Lightness> {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt(
option_counter,
"Let's adjust the color brightness!",
color_mode,
)
.context("failed to print title prompt")?;
writeln!(
io::stdout(),
print_title_prompt(option_counter, "Let's adjust the color brightness!");
println!(
"The colors might be a little bit too {bright_dark} for {light_dark} mode.\n",
bright_dark = match theme {
TerminalTheme::Light => "bright",
TerminalTheme::Dark => "dark",
},
light_dark = theme.as_ref()
)
.context("failed to write message to stdout")?;
);
let color_align = ColorAlignment::Horizontal;
@@ -697,14 +659,10 @@ fn create_config(
}
loop {
writeln!(
io::stdout(),
"\nWhich brightness level looks the best? (Default: {default:.0}% for \
{light_dark} mode)",
println!("\nWhich brightness level looks the best? (Default: {default:.0}% for {light_dark} mode)",
default = f32::from(default_lightness) * 100.0,
light_dark = theme.as_ref()
)
.context("failed to write prompt to stdout")?;
);
let lightness = input(Some("> "))
.context("failed to read input")?
.trim()
@@ -716,12 +674,7 @@ fn create_config(
},
Err(err) => {
debug!(%err, "could not parse lightness");
printc(
"&cUnable to parse lightness value, please enter a lightness value such \
as 45%, .45, or 45",
color_mode,
)
.context("failed to print message")?;
printc!("&cUnable to parse lightness value, please enter a lightness value such as 45%, .45, or 45");
},
}
}
@@ -845,16 +798,11 @@ fn create_config(
}
printc(line.join(" "), color_mode).context("failed to print ascii line")?;
}
writeln!(io::stdout()).context("failed to write to stdout")?;
println!();
}
print_title_prompt(
option_counter,
"Do you want the default logo, or the small logo?",
color_mode,
)
.context("failed to print title prompt")?;
print_title_prompt(option_counter, "Do you want the default logo, or the small logo?");
let opts: Vec<Cow<str>> = ["default", "small"].map(Into::into).into();
let choice = literal_input("Your choice?", &opts[..], "default", true, color_mode)
.context("failed to ask for choice input")
@@ -969,21 +917,11 @@ fn create_config(
}
printc(line.join(" "), color_mode).context("failed to print ascii line")?;
}
writeln!(io::stdout()).context("failed to write to stdout")?;
println!();
}
print_title_prompt(
option_counter,
"Let's choose a color arrangement!",
color_mode,
)
.context("failed to print title prompt")?;
writeln!(
io::stdout(),
"You can choose standard horizontal or vertical alignment, or use one of the random \
color schemes.\nYou can type \"roll\" to randomize again.\n"
)
.context("failed to write message to stdout")?;
print_title_prompt(option_counter, "Let's choose a color arrangement!");
println!("You can choose standard horizontal or vertical alignment, or use one of the random color schemes.\nYou can type \"roll\" to randomize again.\n");
let mut opts: Vec<Cow<str>> = ["horizontal", "vertical", "roll"].map(Into::into).into();
opts.extend((0..random_count).map(|i| format!("random{i}").into()));
let choice = literal_input("Your choice?", &opts[..], "horizontal", true, color_mode)
@@ -1022,42 +960,27 @@ fn create_config(
let select_backend = || -> Result<Backend> {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt(option_counter, "Select a *fetch backend", color_mode)
.context("failed to print title prompt")?;
print_title_prompt(option_counter, "Select a *fetch backend");
// Check if fastfetch is installed
let fastfetch_path = fastfetch_path().ok();
// Check if macchina is installed
#[cfg(feature = "macchina")]
let macchina_path = macchina_path().context("failed to get macchina path")?;
let macchina_path = macchina_path().unwrap_or(None);
printc(
"- &bneofetch&r: Written in bash, &nbest compatibility&r on Unix systems",
color_mode,
)
.context("failed to print message")?;
printc(
format!(
"- &bfastfetch&r: Written in C, &nbest performance&r {installed_not_installed}",
installed_not_installed = fastfetch_path
.map(|path| format!("&a(Installed at {path})", path = path.display()))
.unwrap_or_else(|| "&c(Not installed)".to_owned())
),
color_mode,
)
.context("failed to print message")?;
printc!("- &bneofetch&r: Written in bash, &nbest compatibility&r on Unix systems");
printc!("- &bfastfetch&r: Written in C, &nbest performance&r {}",
fastfetch_path
.map(|path| format!("&a(Installed at {path})", path = path.display()))
.unwrap_or_else(|| "&c(Not installed)".to_owned())
);
#[cfg(feature = "macchina")]
printc(
format!(
"- &bmacchina&r: Written in Rust, &nbest performance&r {installed_not_installed}\n",
installed_not_installed = macchina_path
.map(|path| format!("&a(Installed at {path})", path = path.display()))
.unwrap_or_else(|| "&c(Not installed)".to_owned())
),
color_mode,
)
.context("failed to print message")?;
printc!("- &bmacchina&r: Written in Rust, &nbest performance&r {}\n",
macchina_path
.map(|path| format!("&a(Installed at {path})", path = path.display()))
.unwrap_or_else(|| "&c(Not installed)".to_owned())
);
let choice = literal_input(
"Your choice?",
@@ -1078,10 +1001,56 @@ fn create_config(
backend.as_ref(),
);
//////////////////////////////
// 8. Custom ASCII file
let mut custom_ascii_path: Option<String> = None;
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
let choice = literal_input(
"Do you want to specify a custom ASCII file?",
&["y", "n"],
"n",
true,
color_mode,
)
.context("failed to ask for choice input")?;
if choice == "y" {
loop {
let pth = input(Some("Path to custom ASCII file (must be UTF-8 encoded, empty to skip): "))?.trim().to_owned();
if pth.is_empty() {
printc!("&cNo path entered. Skipping custom ASCII file.");
break;
}
let pth_buf = PathBuf::from(&pth);
if !pth_buf.is_file() {
printc!("&cError: File not found at {pth}");
continue;
}
match fs::read_to_string(&pth_buf) {
Ok(_) => {
custom_ascii_path = Some(pth);
update_title(
&mut title,
&mut option_counter,
"Custom ASCII file",
custom_ascii_path.as_ref().unwrap(),
);
break;
}
Err(e) => {
printc!("&cError: File is not UTF-8 encoded or an unexpected error occurred: {e}");
continue;
}
}
}
}
// Create config
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
let config = Config {
preset,
preset: preset.as_ref().to_string(),
mode: color_mode,
light_dark: Some(theme),
auto_detect_light_dark: Some(det_bg.is_some()),
@@ -1091,6 +1060,7 @@ fn create_config(
args: None,
distro: logo_chosen,
pride_month_disable: false,
custom_ascii_path,
};
debug!(?config, "created config");
+4 -27
View File
@@ -8,7 +8,7 @@ use bpaf::ShellComp;
use bpaf::{construct, long, OptionParser, Parser as _};
use directories::BaseDirs;
use itertools::Itertools as _;
use strum::{VariantArray, VariantNames};
use strum::VariantNames;
use crate::color_util::{color, Lightness};
use crate::presets::Preset;
@@ -18,7 +18,7 @@ use crate::types::{AnsiMode, Backend};
pub struct Options {
pub config: bool,
pub config_file: PathBuf,
pub preset: Option<Preset>,
pub preset: Option<String>,
pub mode: Option<AnsiMode>,
pub backend: Option<Backend>,
pub args: Option<Vec<String>>,
@@ -55,7 +55,7 @@ pub fn options() -> OptionParser<Options> {
let preset = long("preset")
.short('p')
.help(&*format!(
"Use preset
"Use preset or comma-separated color list or comma-separated hex colors (e.g., \"#ff0000,#00ff00,#0000ff\")
PRESET={{{presets}}}",
presets = <Preset as VariantNames>::VARIANTS
.iter()
@@ -65,30 +65,7 @@ PRESET={{{presets}}}",
.argument::<String>("PRESET");
#[cfg(feature = "autocomplete")]
let preset = preset.complete(complete_preset);
let preset = preset
.parse(|s| {
Preset::from_str(&s)
.or_else(|e| {
if s == "random" {
let mut rng = fastrand::Rng::new();
Ok(*rng
.choice(<Preset as VariantArray>::VARIANTS)
.expect("preset iterator should not be empty"))
} else {
Err(e)
}
})
.with_context(|| {
format!(
"PRESET should be one of {{{presets}}}",
presets = <Preset as VariantNames>::VARIANTS
.iter()
.chain(iter::once(&"random"))
.join(",")
)
})
})
.optional();
let preset = preset.optional();
let mode = long("mode")
.short('m')
.help(&*format!(
+9 -7
View File
@@ -403,18 +403,20 @@ where
Ok(dst)
}
#[macro_export]
macro_rules! printc {
($($arg:tt)*) => {
println!("{}", 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
S: AsRef<str>,
{
writeln!(
io::stdout(),
"{msg}",
msg = color(format!("{msg}&r", msg = msg.as_ref()), mode)
.context("failed to color message")?
)
.context("failed to write message to stdout")
println!("{msg}", msg = color(format!("{msg}&r", msg = msg.as_ref()), mode).context("failed to color message")?);
Ok(())
}
/// Clears screen using ANSI escape codes.
+2 -2
View File
@@ -2,12 +2,11 @@ use serde::{Deserialize, Serialize};
use crate::color_util::Lightness;
use crate::neofetch_util::ColorAlignment;
use crate::presets::Preset;
use crate::types::{AnsiMode, Backend, TerminalTheme};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config {
pub preset: Preset,
pub preset: String,
pub mode: AnsiMode,
pub auto_detect_light_dark: Option<bool>,
pub light_dark: Option<TerminalTheme>,
@@ -19,6 +18,7 @@ pub struct Config {
pub args: Option<Vec<String>>,
pub distro: Option<String>,
pub pride_month_disable: bool,
pub custom_ascii_path: Option<String>,
}
impl Config {
+9 -32
View File
@@ -113,16 +113,12 @@ where
};
if let Some(selected) = find_selection(&selection, options) {
writeln!(io::stdout()).context("failed to write to stdout")?;
println!();
return Ok(selected);
} else {
let options_text = options.iter().map(AsRef::as_ref).join("|");
writeln!(
io::stdout(),
"Invalid selection! {selection} is not one of {options_text}"
)
.context("failed to write message to stdout")?;
println!("Invalid selection! {selection} is not one of {options_text}");
}
}
@@ -285,7 +281,7 @@ pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec<String>>)
}
/// Gets distro ascii width and height, ignoring color code.
pub fn ascii_size<S>(asc: S) -> Result<(u8, u8)>
pub fn ascii_size<S>(asc: S) -> Result<(u16, u16)>
where
S: AsRef<str>,
{
@@ -307,25 +303,11 @@ where
return Ok((0, 0));
}
let width = asc
.lines()
.map(|line| line.graphemes(true).count())
.max()
let width = asc.lines()
.map(|line| line.graphemes(true).count()).max()
.expect("line iterator should not be empty");
let width: u8 = width.try_into().with_context(|| {
format!(
"`asc` should not have more than {limit} characters per line",
limit = u8::MAX
)
})?;
let height = asc.lines().count();
let height: u8 = height.try_into().with_context(|| {
format!(
"`asc` should not have more than {limit} lines",
limit = u8::MAX
)
})?;
let width: u16 = width.try_into().context("ascii art width should fit in u16")?;
let height: u16 = asc.lines().count().try_into().context("ascii art height should fit in u16")?;
Ok((width, height))
}
@@ -486,13 +468,8 @@ pub fn get_distro_name(backend: Backend) -> Result<String> {
match backend {
Backend::Neofetch => run_neofetch_command_piped(&["ascii_distro_name"])
.context("failed to get distro name from neofetch"),
Backend::Fastfetch => Ok(run_fastfetch_command_piped(&[
"--logo",
"none",
"-s",
"OS",
"--disable-linewrap",
]).context("failed to get distro name from fastfetch")?.replace("OS: ", "")),
Backend::Fastfetch => Ok(run_fastfetch_command_piped(&["--logo", "none", "-c", "none", "-s", "OS",])
.context("failed to get distro name from fastfetch")?.replace("OS: ", "")),
#[cfg(feature = "macchina")]
Backend::Macchina => {
// Write ascii art to temp file
+73
View File
@@ -63,6 +63,8 @@ pub enum Preset {
Aromantic,
Fictosexual,
Aroace1,
Aroace2,
@@ -111,6 +113,10 @@ pub enum Preset {
Genderflux,
Nullflux,
Hypergender, Hyperboy, Hypergirl, Hyperandrogyne, Hyperneutrois,
Finsexual,
Unlabeled1,
@@ -165,6 +171,8 @@ pub enum Preset {
Twink,
Adipophilia,
Kenochoric,
Veldian,
@@ -221,6 +229,10 @@ pub enum Preset {
/// Meme flag
Band,
Libragender, Librafeminine, Libramasculine, Libraandrogyne, Libranonbinary,
Fluidfluxa, Fluidfluxb,
}
#[derive(Clone, Eq, PartialEq, Debug)]
@@ -304,6 +316,11 @@ impl Preset {
"#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000",
]),
// https://orientation.fandom.com/wiki/Fictosexual
Self::Fictosexual => ColorProfile::from_hex_colors(vec![
"#000000", "#C4C4C4", "#A349A5", "#C4C4C4", "#000000",
]),
// sourced from https://flag.library.lgbt/flags/aroace/
Self::Aroace1 => ColorProfile::from_hex_colors(vec![
"#E28C00", "#ECCD00", "#FFFFFF", "#62AEDC", "#203856",
@@ -433,6 +450,30 @@ impl Preset {
"f47694", "f2a2b9", "cecece", "7ce0f7", "3ecdf9", "fff48d",
]),
Self::Nullflux => ColorProfile::from_hex_colors(vec![
"#0B0C0E", "#A28DB9", "#E1D4EF", "#F0E6DD", "#665858",
]),
Self::Hypergender => ColorProfile::from_hex_colors(vec![
"#EFEFEF", "#FFFFFF", "#FBFF75", "#000000", "#FBFF75", "#FFFFFF", "#EFEFEF",
]),
Self::Hyperboy => ColorProfile::from_hex_colors(vec![
"#EFEFEF", "#FFFFFF", "#74D7FE", "#000000", "#74D7FE", "#FFFFFF", "#EFEFEF",
]),
Self::Hypergirl => ColorProfile::from_hex_colors(vec![
"#EFEFEF", "#FFFFFF", "#FC76D3", "#000000", "#FC76D3", "#FFFFFF", "#EFEFEF",
]),
Self::Hyperandrogyne => ColorProfile::from_hex_colors(vec![
"#EFEFEF", "#FFFFFF", "#BB83FF", "#000000", "#BB83FF", "#FFFFFF", "#EFEFEF",
]),
Self::Hyperneutrois => ColorProfile::from_hex_colors(vec![
"#EFEFEF", "#FFFFFF", "#BAFA74", "#000000", "#BAFA74", "#FFFFFF", "#EFEFEF",
]),
// sourced from https://lgbtqia.wiki/wiki/Finsexual
Self::Finsexual => ColorProfile::from_hex_colors(vec![
"#B18EDF", "#D7B1E2", "#F7CDE9", "#F39FCE", "#EA7BB3",
@@ -543,6 +584,11 @@ impl Preset {
// colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg
Self::Twink => ColorProfile::from_hex_colors(vec!["#FFB2FF", "#FFFFFF", "#FFFF81"]),
// https://en.wikipedia.org/wiki/File:FatFetishFlag.png
Self::Adipophilia => ColorProfile::from_hex_colors(vec![
"#000000", "#E16180", "#FFF9BE", "#603E41", "#000000",
]),
Self::Kenochoric => {
ColorProfile::from_hex_colors(vec!["#000000", "#2E1569", "#824DB7", "#C7A1D6"])
},
@@ -645,6 +691,33 @@ impl Preset {
"#2670C0", "#F5BD00", "#DC0045", "#E0608E"
]),
Self::Libragender => ColorProfile::from_hex_colors(vec![
"#000000", "#808080", "#92D8E9", "#FFF544", "#FFB0CA", "#808080", "#000000"
]),
Self::Librafeminine => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#C6568F", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Libramasculine => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#56C5C5", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Libraandrogyne => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#9186B1", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Libranonbinary => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#FFF987", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Fluidfluxa => ColorProfile::from_hex_colors(vec![
"#ff115f", "#a34aa3", "#00a4e7", "#ffdf00", "#000000", "#ffed71", "#85daff", "#dbadda", "#fe8db1"
]),
Self::Fluidfluxb => ColorProfile::from_hex_colors(vec![
"#c6d1d2", "#f47b9d", "#f09f9b", "#e3f09e", "#75eeea", "#52d2ed", "#c6d1d2"
]),
})
.expect("preset color profiles should be valid")
}
+25 -85
View File
@@ -47,58 +47,32 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
};
let text = &TEXT_ASCII[1..TEXT_ASCII.len().checked_sub(1).unwrap()];
let (text_width, text_height) =
ascii_size(text).expect("text ascii should have valid width and height");
let (text_w, text_h) = ascii_size(text)?;
let (text, text_width, text_height) = {
const TEXT_BORDER_WIDTH: u16 = 2;
const NOTICE_BORDER_WIDTH: u16 = 1;
const VERTICAL_MARGIN: u16 = 1;
let notice_w = NOTICE.len();
let notice_w: u8 = notice_w
.try_into()
.expect("`NOTICE` width should fit in `u8`");
let notice_h = NOTICE.lines().count();
let notice_h: u8 = notice_h
.try_into()
.expect("`NOTICE` height should fit in `u8`");
let notice_w: u16 = NOTICE.len().try_into()?;
let notice_h: u16 = NOTICE.lines().count().try_into()?;
let term_w_min = cmp::max(
u16::from(text_width)
.checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap())
.unwrap(),
u16::from(notice_w)
.checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap())
.unwrap(),
text_w + TEXT_BORDER_WIDTH * 2,
notice_w + NOTICE_BORDER_WIDTH * 2,
);
let term_h_min = u16::from(text_height)
.checked_add(notice_h.into())
.unwrap()
.checked_add(VERTICAL_MARGIN.checked_mul(2).unwrap())
.unwrap();
let term_h_min = u16::from(text_h) + notice_h + VERTICAL_MARGIN * 2;
if w.get() >= term_w_min && h.get() >= term_h_min {
(text, text_width, text_height)
(text, text_w, text_h)
} else {
let text = &TEXT_ASCII_SMALL[1..TEXT_ASCII_SMALL.len().checked_sub(1).unwrap()];
let (text_width, text_height) =
ascii_size(text).expect("text ascii should have valid width and height");
let (text_w, text_h) = ascii_size(text)?;
let term_w_min = cmp::max(
u16::from(text_width)
.checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap())
.unwrap(),
u16::from(notice_w)
.checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap())
.unwrap(),
text_w + TEXT_BORDER_WIDTH * 2,
notice_w + NOTICE_BORDER_WIDTH * 2,
);
let term_h_min = u16::from(text_height)
.checked_add(notice_h.into())
.unwrap()
.checked_add(VERTICAL_MARGIN.checked_mul(2).unwrap())
.unwrap();
let term_h_min = text_h + notice_h + VERTICAL_MARGIN * 2;
if w.get() < term_w_min || h.get() < term_h_min {
return Err(anyhow!(
"terminal size should be at least ({term_w_min} * {term_h_min})"
));
return Err(anyhow!("terminal size should be at least ({term_w_min} * {term_h_min})"));
}
(text, text_width, text_height)
(text, text_w, text_h)
}
};
let text_lines: Vec<&str> = text.lines().collect();
@@ -165,8 +139,7 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.rem_euclid(colors.len())]
.to_ansi_string(color_mode, ForegroundBackground::Background),
fg = fg.to_ansi_string(color_mode, ForegroundBackground::Foreground)
)
.unwrap();
)?;
// Loop over the width
for x in 0..w.get() {
@@ -176,27 +149,11 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.wrapping_add_signed((2.0 * (y as f64 + 0.5 * frame as f64).sin()) as isize);
let y_text = text_start_y <= y && y < text_end_y;
let border = 1u16
.checked_add(
if y == text_start_y || y == text_end_y.checked_sub(1).unwrap() {
0
} else {
1
},
)
.unwrap();
let text_bounds_x1 = text_start_x
.checked_sub(border)
.expect("`text_start_x - border` should not underflow `u16`");
let text_bounds_x2 = text_end_x
.checked_add(border)
.expect("`text_end_x + border` should not overflow `u16`");
let notice_bounds_x1 = notice_start_x
.checked_sub(1)
.expect("`notice_start_x - 1` should not underflow `u16`");
let notice_bounds_x2 = notice_end_x
.checked_add(1)
.expect("`notice_end_x + 1` should not overflow `u16`");
let border = 1u16 + if y == text_start_y || y == (text_end_y - 1) { 0 } else { 1 };
let text_bounds_x1 = text_start_x - border;
let text_bounds_x2 = text_end_x - border;
let notice_bounds_x1 = notice_start_x - 1;
let notice_bounds_x2 = notice_end_x - 1;
// If it's a switching point
if idx.rem_euclid(NonZeroUsize::from(block_width).get()) == 0
@@ -215,19 +172,9 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
{
let c: LinSrgba = c.with_alpha(1.0).into_linear();
let c = Srgb::<u8>::from_linear(c.overlay(black).without_alpha());
write!(
buf,
"{bg}",
bg = c.to_ansi_string(color_mode, ForegroundBackground::Background),
)
.unwrap();
write!(buf, "{bg}", bg = c.to_ansi_string(color_mode, ForegroundBackground::Background))?;
} else {
write!(
buf,
"{bg}",
bg = c.to_ansi_string(color_mode, ForegroundBackground::Background),
)
.unwrap();
write!(buf, "{bg}", bg = c.to_ansi_string(color_mode, ForegroundBackground::Background))?;
}
}
@@ -240,8 +187,7 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.chars()
.nth(usize::from(x.checked_sub(text_start_x).unwrap()))
.unwrap(),
)
.unwrap();
)?;
} else if y == notice_y && notice_start_x <= x && x < notice_end_x {
write!(
buf,
@@ -250,21 +196,15 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.chars()
.nth(usize::from(x.checked_sub(notice_start_x).unwrap()))
.unwrap(),
)
.unwrap();
)?;
} else {
write!(buf, " ").unwrap();
write!(buf, " ")?;
}
}
// New line if it isn't the last line
if y != h.get().checked_sub(1).unwrap() {
writeln!(
buf,
"{reset}",
reset = color("&r", color_mode).expect("reset should be valid"),
)
.unwrap();
writeln!(buf, "{reset}", reset = color("&r", color_mode)?)?;
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH VERSION: "1" "August 2025" "Version: 2.0.1" "User Commands"
.TH VERSION: "1" "September 2025" "Version: 2.0.2" "User Commands"
.SH NAME
Version: \- manual page for Version: 2.0.1
Version: \- manual page for Version: 2.0.2
.SH SYNOPSIS
.B hyfetch
[\fI\,-c\/\fR] [\fI\,-C=CONFIG_FILE\/\fR] [\fI\,-p=PRESET\/\fR] [\fI\,-m=MODE\/\fR] [\fI\,-b=BACKEND\/\fR] [\fI\,--args=ARGS\/\fR] [\fI\,--c-scale=\/\fR
@@ -21,7 +21,7 @@ Use another config file
.TP
\fB\-p\fR, \fB\-\-preset\fR=\fI\,PRESET\/\fR
Use preset
PRESET={rainbow,transgender,nonbinary,xenogender,agender,queer,genderfluid,bisexual,pansexual,polysexual,omnisexual,omniromantic,gay\-men,lesbian,abrosexual,asexual,aromantic,aroace1,aroace2,aroace3,greysexual,autosexual,intergender,greygender,akiosexual,bigender,demigender,demiboy,demigirl,transmasculine,transfeminine,genderfaun,demifaun,genderfae,demifae,neutrois,biromantic1,autoromantic,boyflux2,girlflux,genderflux,finsexual,unlabeled1,unlabeled2,pangender,pangender.contrast,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,caninekin,plural,fraysexual,bear,butch,leather,otter,twink,kenochoric,veldian,solian,lunian,polyam,sapphic,androgyne,interprogress,progress,intersex,old\-polyam,equal\-rights,drag,pronounfluid,pronounflux,exipronoun,neopronoun,neofluid,genderqueer,cisgender,baker,beiyang,burger,throatlozenges,band,random}
PRESET={rainbow,transgender,nonbinary,xenogender,agender,queer,genderfluid,bisexual,pansexual,polysexual,omnisexual,omniromantic,gay\-men,lesbian,abrosexual,asexual,aromantic,fictosexual,aroace1,aroace2,aroace3,greysexual,autosexual,intergender,greygender,akiosexual,bigender,demigender,demiboy,demigirl,transmasculine,transfeminine,genderfaun,demifaun,genderfae,demifae,neutrois,biromantic1,autoromantic,boyflux2,girlflux,genderflux,nullflux,hypergender,hyperboy,hypergirl,hyperandrogyne,hyperneutrois,finsexual,unlabeled1,unlabeled2,pangender,pangender.contrast,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,caninekin,plural,fraysexual,bear,butch,leather,otter,twink,adipophilia,kenochoric,veldian,solian,lunian,polyam,sapphic,androgyne,interprogress,progress,intersex,old\-polyam,equal\-rights,drag,pronounfluid,pronounflux,exipronoun,neopronoun,neofluid,genderqueer,cisgender,baker,beiyang,burger,throatlozenges,band,random}
.TP
\fB\-m\fR, \fB\-\-mode\fR=\fI\,MODE\/\fR
Color mode MODE={8bit,rgb}
+2 -2
View File
@@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH NEOFETCH "1" "August 2025" "Neofetch 8.0.1" "User Commands"
.TH NEOFETCH "1" "September 2025" "Neofetch 8.0.2" "User Commands"
.SH NAME
Neofetch \- manual page for Neofetch 8.0.1
Neofetch \- manual page for Neofetch 8.0.2
.SH SYNOPSIS
.B neofetch
\fI\,func_name --option "value" --option "value"\/\fR
+1 -1
View File
@@ -1 +1 @@
VERSION = '2.0.1'
VERSION = '2.0.2'
+11 -3
View File
@@ -135,9 +135,17 @@ class RGB:
:return: RGB object
"""
hex = hex.lstrip("#")
r = int(hex[0:2], 16)
g = int(hex[2:4], 16)
b = int(hex[4:6], 16)
if len(hex) == 6:
r = int(hex[0:2], 16)
g = int(hex[2:4], 16)
b = int(hex[4:6], 16)
elif len(hex) == 3:
r = int(hex[0], 16)
g = int(hex[1], 16)
b = int(hex[2], 16)
else:
raise ValueError(f"Error: invalid hex length")
return cls(r, g, b)
def to_ansi_rgb(self, foreground: bool = True) -> str:
+1 -1
View File
@@ -3,7 +3,7 @@
from . import AsciiArt
nixos_small = AsciiArt(match=r'''"nixos_small"''', color='4 6', ascii=r"""
${c1} \\ \\ //
${c1} \\ \\ //
==\\__\\/ //
// \\//
==// //==
+67 -6
View File
@@ -5,6 +5,7 @@ import argparse
import datetime
import importlib.util
import json
import os
import random
import traceback
from itertools import permutations, islice
@@ -17,7 +18,7 @@ from .constants import *
from .font_logo import get_font_logo
from .models import Config
from .neofetch_util import *
from .presets import PRESETS
from .presets import PRESETS, ColorProfile
def check_config(path) -> Config:
@@ -318,9 +319,49 @@ def create_config() -> Config:
backend = select_backend()
update_title('Selected backend', backend)
##############################
# 7. Custom ASCII file
custom_ascii_path = None
clear_screen(title)
if literal_input('Do you want to specify a custom ASCII file?', ['y', 'n'], 'n') == 'y':
while True:
path_input = input('Path to custom ASCII file (must be .txt and UTF-8 encoded): ').strip()
if not path_input:
printc('&cNo path entered. Skipping custom ASCII file.')
break
custom_path = Path(path_input)
if not custom_path.is_file():
printc(f'&cError: File not found at {path_input}')
if literal_input('Try again?', ['y', 'n'], 'y') == 'n':
break
continue
if not custom_path.suffix == '.txt':
printc(f'&cError: File must have a .txt extension. Found {custom_path.suffix}')
if literal_input('Try again?', ['y', 'n'], 'y') == 'n':
break
continue
try:
custom_path.read_text('utf-8')
custom_ascii_path = str(custom_path)
update_title('Custom ASCII file', custom_ascii_path)
break
except UnicodeDecodeError:
printc(f'&cError: File is not UTF-8 encoded.')
if literal_input('Try again?', ['y', 'n'], 'y') == 'n':
break
continue
except Exception as e:
printc(f'&cAn unexpected error occurred: {e}')
if literal_input('Try again?', ['y', 'n'], 'y') == 'n':
break
continue
# Create config
clear_screen(title)
c = Config(preset, color_system, light_dark, lightness, color_alignment, backend)
c = Config(preset, color_system, light_dark, lightness, color_alignment, backend, custom_ascii_path=custom_ascii_path)
# Save config
print()
@@ -338,7 +379,7 @@ def create_parser() -> argparse.ArgumentParser:
parser.add_argument('-c', '--config', action='store_true', help=color(f'Configure hyfetch'))
parser.add_argument('-C', '--config-file', dest='config_file', default=CONFIG_PATH, help=f'Use another config file')
parser.add_argument('-p', '--preset', help=f'Use preset', choices=list(PRESETS.keys()) + ['random'])
parser.add_argument('-p', '--preset', help=f'Use preset or comma-separated hex color list (e.g., "#ff0000,#00ff00,#0000ff")')
parser.add_argument('-m', '--mode', help=f'Color mode', choices=['8bit', 'rgb'])
parser.add_argument('-b', '--backend', help=f'Choose a *fetch backend', choices=['qwqfetch', 'neofetch', 'fastfetch', 'fastfetch-old'])
parser.add_argument('--args', help=f'Additional arguments pass-through to backend')
@@ -382,7 +423,7 @@ def run():
GLOBAL_CFG.use_overlay = args.overlay
if args.version:
print(f'Version is {VERSION}')
print(f'Version is 1.99.∞\n(python hyfetch legacy mode)')
return
# Ensure git bash for windows
@@ -451,7 +492,21 @@ def run():
GLOBAL_CFG.is_light = config.light_dark == 'light'
# Get preset
preset = PRESETS.get(config.preset)
preset = None
if config.preset in PRESETS:
preset = PRESETS.get(config.preset)
elif '#' in config.preset:
colors = [color.strip() for color in config.preset.split(',')]
for color in colors:
if not (color.startswith('#') and len(color) in [4, 7] and all(c in '0123456789abcdefABCDEF' for c in color[1:])):
print(f'Error: invalid hex color "{color}"')
preset = ColorProfile(colors)
else:
print(f'Preset should be a comma-separated list of hex colors, or one of the following: {', '.join(sorted(PRESETS.keys()))}')
if preset is None:
exit(1)
# Lighten (args > config)
if args.scale:
@@ -463,8 +518,14 @@ def run():
# Run
try:
asc = get_distro_ascii() if not args.ascii_file else Path(args.ascii_file).read_text("utf-8")
if config.custom_ascii_path:
asc = Path(config.custom_ascii_path).read_text("utf-8")
elif args.ascii_file:
asc = Path(args.ascii_file).read_text("utf-8")
else:
asc = get_distro_ascii()
asc = config.color_align.recolor_ascii(asc, preset)
asc = '\n'.join(asc.split('\n')[1:])
neofetch_util.run(asc, config.backend, config.args or '')
except Exception as e:
print(f'Error: {e}')
+1
View File
@@ -20,6 +20,7 @@ class Config:
distro: str | None = None
pride_month_shown: list[int] = field(default_factory=list) # This is deprecated, see issue #136
pride_month_disable: bool = False
custom_ascii_path: str | None = None
@classmethod
def from_dict(cls, d: dict):
+108
View File
@@ -300,6 +300,9 @@ PRESETS: dict[str, ColorProfile] = {
'#ABABAB',
'#000000'
]),
# https://orientation.fandom.com/wiki/Fictosexual
'fictosexual': ColorProfile(["#000000", "#C4C4C4", "#A349A5", "#C4C4C4", "#000000"]),
# aroace1 sourced from https://flag.library.lgbt/flags/aroace/
'aroace1': ColorProfile([
@@ -550,6 +553,27 @@ PRESETS: dict[str, ColorProfile] = {
"3ecdf9",
"fff48d",
]),
# https://lgbtqia.wiki/wiki/Gendernull
'nullflux': ColorProfile([
'#0B0C0E', '#A28DB9', '#E1D4EF', '#F0E6DD', '#665858',
]),
'hypergender': ColorProfile([
"#EFEFEF", "#FFFFFF", "#FBFF75", "#000000", "#FBFF75", "#FFFFFF", "#EFEFEF",
]),
'hyperboy': ColorProfile([
"#EFEFEF", "#FFFFFF", "#74D7FE", "#000000", "#74D7FE", "#FFFFFF", "#EFEFEF",
]),
'hypergirl': ColorProfile([
"#EFEFEF", "#FFFFFF", "#FC76D3", "#000000", "#FC76D3", "#FFFFFF", "#EFEFEF",
]),
'hyperandrogyne': ColorProfile([
"#EFEFEF", "#FFFFFF", "#BB83FF", "#000000", "#BB83FF", "#FFFFFF", "#EFEFEF",
]),
'hyperneutrois': ColorProfile([
"#EFEFEF", "#FFFFFF", "#BAFA74", "#000000", "#BAFA74", "#FFFFFF", "#EFEFEF",
]),
"finsexual": ColorProfile([
"#B18EDF",
@@ -751,6 +775,9 @@ PRESETS: dict[str, ColorProfile] = {
'#FFFFFF',
'#FFFF81',
]),
# https://en.wikipedia.org/wiki/File:FatFetishFlag.png
'adipophilia': ColorProfile(["#000000", "#E16180", "#FFF9BE", "#603E41", "#000000"]),
'kenochoric': ColorProfile([
'#000000',
@@ -984,4 +1011,85 @@ PRESETS: dict[str, ColorProfile] = {
"#dc0045",
"#e0608e"
]),
# Adding libragender flags https://lgbtqia.wiki/wiki/Libragender
# Sourced from https://lgbtqia.wiki/wiki/Libragender
'libragender': ColorProfile([
"#000000",
"#808080",
"#92D8E9",
"#FFF544",
"#FFB0CA",
"#808080",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Librafeminine
'librafeminine': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#C6568F",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Libramasculine
'libramasculine': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#56C5C5",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Librandrogyne
'libraandrogyne': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#9186B1",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Libranonbinary
'libranonbinary': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#FFF987",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Adding Fluidflux flags - ObsoleteDev
# Sourced from https://gender.fandom.com/wiki/Fluidflux?file=FC90B24D-CA36-4FE2-A752-C9ABFC65E332.jpeg
'fluidflux A': ColorProfile([
"#ff115f",
"#a34aa3",
"#00a4e7",
"#ffdf00",
"#000000",
"#ffed71",
"#85daff",
"#dbadda",
"#fe8db1"
]),
'fluidflux B': ColorProfile([
"#c6d1d2",
"#f47b9d",
"#f09f9b",
"#e3f09e",
"#75eeea",
"#52d2ed",
"#c6d1d2"
]),
}
+4 -1
View File
@@ -1,3 +1,4 @@
import os
import platform
import subprocess
import sys
@@ -12,7 +13,9 @@ def run_rust():
pd = Path(__file__).parent.joinpath('rust')
pd = pd.joinpath('hyfetch.exe' if platform.system() == 'Windows' else 'hyfetch')
if not pd.exists():
printc('&cThe rust executable is not found, falling back to python...')
if 'HYFETCH_DONT_WARN_RUST' not in os.environ:
printc('&cThe executable for hyfetch v2 (rust) is not found, falling back to legacy v1.99.∞ (python).\n'
'You can add environment variable HYFETCH_DONT_WARN_RUST=1 to suppress this warning.\n')
run_py()
return
+210 -199
View File
@@ -28,7 +28,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
version=8.0.1
version=8.0.2
# Fallback to a value of '5' for shells which support bash
# but do not set the 'BASH_' shell variables (osh).
@@ -225,7 +225,7 @@ package_managers="on"
# Show separate user and system packages for supported package managers
#
#
# Default: 'on'
# Values: 'on', 'off'
# Flag: --package_separate
@@ -1145,7 +1145,7 @@ get_distro() {
on|tiny) distro="LindowsOS" ;;
*) distro="$(awk '/Version/ {print $2,$3}' /etc/lindowsos-version)"
esac
elif [[ -f /etc/astra_version ]]; then
distro="Astra Linux"
distro_version="$(sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' < /etc/astra_version)"
@@ -1349,7 +1349,7 @@ get_distro() {
windows_version_verbose=$(trim "${windows_version_verbose/REG_SZ}")
buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_)
windows_version_verbose=$(trim "${windows_version_verbose/Windows}")
windows_buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_)
windows_buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_)
windows_buildnumber=${buildnumber/CurrentBuildNumber}
windows_buildnumber=${buildnumber/REG_SZ}
if [[ "$windows_version_verbose" == *"10"* ]] && (( windows_buildnumber >= 22000 )); then
@@ -1823,6 +1823,10 @@ get_model() {
iPod5,1): "iPod touch 5G" ;;
iPod7,1): "iPod touch 6G" ;;
iPod9,1): "iPod touch 7G" ;;
AppleTV2,1): "Apple TV 2" ;;
AppleTV3,1): "Apple TV 3" ;;
AppleTV3,2): "Apple TV 3 (2013)" ;;
esac
model=$_
@@ -1936,7 +1940,7 @@ get_kernel() {
*) kernel=${ver} ;;
esac
return
}
}
# In Interix the Kernel version is commonly comprised of the host OS build number,
# OS architecture and Interix build number
@@ -2193,7 +2197,7 @@ get_packages() {
has radula && tot radula -cl
# https://github.com/birb-linux/birb
has birb && tot birb --list-installed
has port && pkgs_h=1 tot port installed && ((packages-=1))
# Using the dnf package cache is much faster than rpm.
@@ -2324,7 +2328,7 @@ get_packages() {
has snap && ps -e | grep -qFm 1 snapd >/dev/null && \
pkgs_h=1 tot snap list && ((packages-=1))
# Get AppImageLauncher Application directory,
# Get AppImageLauncher Application directory,
# See: https://github.com/TheAssassin/AppImageLauncher/wiki/Configuration#settings-file
manager=appimage && has appimaged || [ -f "$HOME"/.config/appimagelauncher.cfg ] &&
if [ -f "$HOME"/.config/appimagelauncher.cfg ]; then
@@ -2336,7 +2340,7 @@ get_packages() {
AIDIR="$HOME/.local/bin"
fi &&
dir "$AIDIR/*.[Aa]pp[Ii]mage \
$ALDIR/*.[Aa]pp[Ii]mage"
$ALDIR/*.[Aa]pp[Ii]mage"
# Has devbox & is initialized
has devbox && [[ "$(devbox global list)" != *"not activated"* ]] && tot devbox global list
@@ -2519,7 +2523,7 @@ get_editor() {
esac
editor_v="${editor_v/$'\n'*}"
editor_v="${editor_v/Version: }"
# Only show editor name if the version string doesn't contain it
echo "$editor_v" | grep -i "$editor_name" &> /dev/null && editor_name=""
editor=${editor_name}${editor_v:+ }${editor_v}
@@ -2983,7 +2987,7 @@ get_wm_theme() {
wm_theme=$(leftwm-theme status | grep "Your current theme" | sed -e 's/Your current theme is //g' -e 's/\,.*$//g')
fi
;;
fly-wm)
fly_config_file
if grep -q 'DecorTheme' "${fly_config_file}" 2>/dev/null; then
@@ -3232,6 +3236,10 @@ END
"iPad7,"[1-4]): "Apple A10X Fusion (6) @ 2.39GHz" ;;
"iPad8,"[1-8]): "Apple A12X Bionic (8) @ 2.49GHz" ;;
"iPad8,9" | "iPad8,1"[0-2]): "Apple A12Z Bionic (8) @ 2.49GHz" ;;
"AppleTV2,1"): "Apple A4 (1) @ 1GHz" ;;
"AppleTV3,1"): "Apple A5 (1) @ 1GHz" ;;
"AppleTV3,2"): "Apple A5 (1) @ 1GHz" ;;
esac
cpu="$_"
;;
@@ -3537,10 +3545,12 @@ get_gpu() {
"iPhone OS")
case $kernel_machine in
"iPhone1,"[1-2]): "PowerVR MBX Lite 3D" ;;
"iPhone2,1" | "iPhone3,"[1-3] | "iPod3,1" | "iPod4,1" | "iPad1,"[1-2]):
"iPhone2,1" | "iPhone3,"[1-3] | "iPod3,1" | "iPod4,1" | "iPad1,"[1-2] | "AppleTV2,1"):
"PowerVR SGX535"
;;
"iPhone4,1" | "iPad2,"[1-7] | "iPod5,1"): "PowerVR SGX543MP2" ;;
"iPhone4,1" | "iPad2,"[1-7] | "iPod5,1" | "AppleTV3,"[1-2]):
"PowerVR SGX543MP2"
;;
"iPhone5,"[1-4]): "PowerVR SGX543MP3" ;;
"iPhone6,"[1-2] | "iPad4,"[1-9]): "PowerVR G6430" ;;
"iPhone7,"[1-2] | "iPod7,1" | "iPad5,"[1-2]): "PowerVR GX6450" ;;
@@ -4167,6 +4177,7 @@ get_resolution() {
"iPad13,"[1-2] | "iPad13,1"[6-9]): "1640x2360" ;;
"iPad8,"[1-4] | "iPad8,"[9-10] | "iPad13,"[4-7] | "iPad14,"[3-6]): "1668x2388" ;;
"iPad6,"[7-8] | "iPad7,"[1-2] | "iPad8,"[5-8] | "iPad8,1"[1-2] | "iPad13,"[8-9] | "iPad13,1"[0-1] | "iPad14,"[5-6]): "2048x2732" ;;
"AppleTV"*) return ;;
esac
resolution="$_"
;;
@@ -7440,7 +7451,7 @@ EOF
;;
"ArseLinux"*)
set_colors 4 7
set_colors 4 7
read -rd '' ascii_data <<'EOF'
${c1}
⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
@@ -7972,7 +7983,7 @@ EOF
"aerOS"*)
set_colors fg 0 0 0
read -rd '' ascii_data <<'EOF'
${c1}
${c1}
ooo OOO OOO ooo
oOO OOo
oOO OOo
@@ -8192,23 +8203,23 @@ EOF
"azos"*)
set_colors 6 1
read -rd '' ascii_data <<'EOF'
${c1} ////. ${c2} (((((
${c1} //////// ${c2} @((((((((
${c1} //////// ${c2} @((((((((
${c1} //////// /////// ${c2} ((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ////// ${c2} (((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} ((((((( ((((((((( @((((((((
${c1} //////// ///////// /// ${c2} ( ((((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ${c2} &(((((((( @((((((((
${c1} //////// ////// ${c2} @(((( @((((((((
${c1} //////// ${c2} @((((((((
${c1} //////// ${c2} @((((((((
${c1} ///// ${c2} (((((
${c1} ////. ${c2} (((((
${c1} //////// ${c2} @((((((((
${c1} //////// ${c2} @((((((((
${c1} //////// /////// ${c2} ((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ////// ${c2} (((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} ((((((( ((((((((( @((((((((
${c1} //////// ///////// /// ${c2} ( ((((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ${c2} &(((((((( @((((((((
${c1} //////// ////// ${c2} @(((( @((((((((
${c1} //////// ${c2} @((((((((
${c1} //////// ${c2} @((((((((
${c1} ///// ${c2} (((((
EOF
;;
@@ -8367,7 +8378,7 @@ EOF
"BlackMesa")
set_colors 1
read -rd '' ascii_data <<'EOF'
${c1}
${c1}
.-;+$XHHHHHHX$+;-.
,;X@@X%/;=----=:/%X@@X/,
=$@@%=. .=+H@X:
@@ -9285,16 +9296,16 @@ ${c2}
${c3}MMM1ua${c2}
${c1}MM${c2}EE ${c3} MMMMM1uazE${c2}
${c1}MM ${c2}EEEE ${c3}M1MM1uazzEn ${c2}EEEE MME
EEEEE ${c3}MMM uazEno ${c2}EEEE
EEEEE ${c3}MMM uazEno ${c2}EEEE
EEEEE${c1}MMMMMMEno~; ${c2}EE E${c2}
EE ${c1}MMMMMMMM~;;E ${c2}MMMMM M ${c2}
E ${c1}MMMMMMMMM ${c2} E E ${c2}
${c1}MMMMMMMMMMM
${c1}MMMMMMMMM ${c2}EE ${c1}
MM1MMMM ${c2}EEE ${c1}
MMMMM
MMM
M
${c1}MMMMMMMMMMM
${c1}MMMMMMMMM ${c2}EE ${c1}
MM1MMMM ${c2}EEE ${c1}
MMMMM
MMM
M
EOF
;;
@@ -9442,24 +9453,24 @@ EOF
"openKylin"*)
set_colors 2 7
read -rd '' ascii_data <<'EOF'
${c1}
/KKK]
KKKKKKK` ]KKKK\
KKKKK/ /KKKKKKKKK\
KKKK/ ,KKKKKKKKKKKK^
,]KKK =KKK` /KKKKKKOOOOOO`
,KKKKKK =KK /` [\OOOOOOO\
\KKKKK =K ,OOOOOOO`
,KKKKK =^ \OOOOOO
,KKKK ^ OOOOOO^
*KKK^ =OOOOO^
OOKK^ OOOOOO^
\OOOK\ /OOOOOO`
OOOOOO] ,OOOOOOO^
,OOOOOOOO\] ,[OOOOOOOOO/
\OOOOOOOOOOOOOOOOOOOOO`
[OOOOOOOOOOOOOOOO/`
,[OOOOOOOOO]
${c1}
/KKK]
KKKKKKK` ]KKKK\
KKKKK/ /KKKKKKKKK\
KKKK/ ,KKKKKKKKKKKK^
,]KKK =KKK` /KKKKKKOOOOOO`
,KKKKKK =KK /` [\OOOOOOO\
\KKKKK =K ,OOOOOOO`
,KKKKK =^ \OOOOOO
,KKKK ^ OOOOOO^
*KKK^ =OOOOO^
OOKK^ OOOOOO^
\OOOK\ /OOOOOO`
OOOOOO] ,OOOOOOO^
,OOOOOOOO\] ,[OOOOOOOOO/
\OOOOOOOOOOOOOOOOOOOOO`
[OOOOOOOOOOOOOOOO/`
,[OOOOOOOOO]
EOF
;;
@@ -9890,21 +9901,21 @@ EOF
"eweOS"*)
set_colors 7 11 9 8 1
read -rd '' ascii_data <<'EOF'
${c2} #####%%%
${c2} ##%%${c3}////${c2}%%%%%${c3}///
${c2} #%%%%${c3}////((((////${c2}%
${c1} *@@@@@@@${c3}/${c5},,,${c3}/////${c5},,,${c2}%${c1}@@@@@@@
${c1} .@@@@@@@@@@@${c3}////////${c2}%%%${c1}@@@@@@@@@@@@
${c1} @@@${c4}...${c1}@@@@@@${c3}////${c2}%%${c3}////${c1}@@@@@@@@@@@@@@@@
${c2} #####%%%
${c2} ##%%${c3}////${c2}%%%%%${c3}///
${c2} #%%%%${c3}////((((////${c2}%
${c1} *@@@@@@@${c3}/${c5},,,${c3}/////${c5},,,${c2}%${c1}@@@@@@@
${c1} .@@@@@@@@@@@${c3}////////${c2}%%%${c1}@@@@@@@@@@@@
${c1} @@@${c4}...${c1}@@@@@@${c3}////${c2}%%${c3}////${c1}@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@ @@@@@@
${c1} @@@ @@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@ @@@@@@
${c1} @@@ @@@
EOF
;;
@@ -12126,7 +12137,7 @@ EOF
"nixos_small")
set_colors 4 6
read -rd '' ascii_data <<'EOF'
${c1} \\\\ \\\\ //
${c1} \\\\ \\\\ //
==\\\\__\\\\/ //
// \\\\//
==// //==
@@ -13034,39 +13045,39 @@ EOF
set_colors 11
read -rd '' ascii_data <<'EOF'
${c1}
'',,, ,,,d,
',, ,,'
', ,.
., '
. .
' .
.. oddddkdlc:;,.. ..
. ............lllc, .
. ....................: .
. . ....................
'. ..........'o........d0XX0.
....lllllllcOOOcllllll............cxlxc...;okkkx.
..................................';lc'...lo.
.'''''''''''''.....................,;,.......
',,,,,,,,,,,,,,,,''...............dkkkd......
',,,,,,,,,,,,,,,,,,,'............;okkkd;....
.,,,,,,,,,,,,,,,,,,,,,............;cll;.....
,,,,,,,,,,,,,,,,,,,,'....................:d,
,,,,,,,,,,,,,,,,,,,....................oxxx:
.,,,,,,,,,,,,,,,,,'..................oxxxxx. .
.,,,,,,,,,,,,,,'.......... ,oxxxxxxx .
.;,,,,,,,,,,,,'.. 'cxxxxxxxxx,
:dl:,'',,'.... .;lxxxxxxxxxd;
,codol:;'. ...,;cldxxxxxxxxxoc.
.:cxxxxxdlccccc:ccldxxxxxxxxxxxxxx::.
.'::dxxxxxxxxxxxxxxxxxxxxxxxd::'.
..,::cdxxxxxxxxxxxxxdc::,..
...,;:::::::;,...
'',,, ,,,d,
',, ,,'
', ,.
., '
. .
' .
.. oddddkdlc:;,.. ..
. ............lllc, .
. ....................: .
. . ....................
'. ..........'o........d0XX0.
....lllllllcOOOcllllll............cxlxc...;okkkx.
..................................';lc'...lo.
.'''''''''''''.....................,;,.......
',,,,,,,,,,,,,,,,''...............dkkkd......
',,,,,,,,,,,,,,,,,,,'............;okkkd;....
.,,,,,,,,,,,,,,,,,,,,,............;cll;.....
,,,,,,,,,,,,,,,,,,,,'....................:d,
,,,,,,,,,,,,,,,,,,,....................oxxx:
.,,,,,,,,,,,,,,,,,'..................oxxxxx. .
.,,,,,,,,,,,,,,'.......... ,oxxxxxxx .
.;,,,,,,,,,,,,'.. 'cxxxxxxxxx,
:dl:,'',,'.... .;lxxxxxxxxxd;
,codol:;'. ...,;cldxxxxxxxxxoc.
.:cxxxxxdlccccc:ccldxxxxxxxxxxxxxx::.
.'::dxxxxxxxxxxxxxxxxxxxxxxxd::'.
..,::cdxxxxxxxxxxxxxdc::,..
...,;:::::::;,...
EOF
;;
"phyOS"*)
set_colors 33 33 7 1
read -rd '' ascii_data <<'EOF'
@@ -13530,26 +13541,26 @@ EOF
"Reborn OS"* | "Reborn"*)
set_colors 0 4 6
read -rd '' ascii_data <<'EOF'
${c1} .======================.
${c1}.#${c2}#*********${c1}%%${c2}*********#${c1}%:
${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%-
${c1}-%${c2}************${c1}%%${c2}************${c1}%=
${c1}+%${c2}******${c1}%%#####${c1}%%#####%%${c2}******${c1}%+
${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%*
${c1}*%${c2}*#${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##.
${c1}.##${c2}*****${c1}#%%%#${c3}*++${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%.
${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%:
${c1} .======================.
${c1}.#${c2}#*********${c1}%%${c2}*********#${c1}%:
${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%-
${c1}-%${c2}************${c1}%%${c2}************${c1}%=
${c1}+%${c2}******${c1}%%#####${c1}%%#####%%${c2}******${c1}%+
${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%*
${c1}*%${c2}*#${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##.
${c1}.##${c2}*****${c1}#%%%#${c3}*++${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%.
${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%:
${c1}-%#${c2}*****${c1}#%${c3}+====*${c1}%${c3}*++++++++*${c1}%#${c3}====+${c1}%#${c2}******${c1}%-
${c1}-%#${c2}*****${c1}#%${c3}+====*${c1}%${c3}*++++++++*${c1}%#${c3}====+${c1}%#${c2}******${c1}%=
${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%-
${c1}.##${c2}*****${c1}#%%%#${c3}*+*${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%:
${c1}.##${c2}**${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##.
${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%*
${c1}+%${c2}******${c1}%%#####%%#####%%${c2}******${c1}%*
${c1}-%${c2}************${c1}%%${c2}************${c1}%=
${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%-
${c1}:%${c2}#*********${c1}%%${c2}*********#${c1}%:
${c1}.======================.
${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%-
${c1}.##${c2}*****${c1}#%%%#${c3}*+*${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%:
${c1}.##${c2}**${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##.
${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%*
${c1}+%${c2}******${c1}%%#####%%#####%%${c2}******${c1}%*
${c1}-%${c2}************${c1}%%${c2}************${c1}%=
${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%-
${c1}:%${c2}#*********${c1}%%${c2}*********#${c1}%:
${c1}.======================.
EOF
;;
@@ -13723,20 +13734,20 @@ EOF
"RhaymOS"*)
set_colors 1
read -rd '' ascii_data <<'EOF'
${c1}
###
#####
####### /########
############# ###########
,########### #### ####(..
#### #### ####* ##########
${c1}
###
#####
####### /########
############# ###########
,########### #### ####(..
#### #### ####* ##########
#### ##### ##### (####
#### ########### ###########
#### ######### ##########
###################################
#####################################
#### ########### ###########
#### ######### ##########
###################################
#####################################
#######################################
EOF
;;
@@ -15046,29 +15057,29 @@ oss${c2}yNMMMNyMMh${c1}sssssssssssssshmmmh${c1}ssssssso
.-\+oossssoo+/-.
EOF
;;
"Floflis"*)
set_colors 1 7 3
read -rd '' ascii_data <<'EOF'
\e[96m ,▄▄▄▌▓▓███▓▓▌▄▄▄,
,▄▒▓███████████████████▓▄▄
▄▓███████████████████████████▌
▓███████████████████████████████
, ╙▓████████████████████████████▀ ▄
╓█▓▄ ╙▀▓████████████████████▀▀` ,▄██▓
╓█████▌▄, '▀▀▀▀▓▓▓▓▓▓▀▀Å╙` ▄▄▓█████▌
\e[96m ,▄▄▄▌▓▓███▓▓▌▄▄▄,
,▄▒▓███████████████████▓▄▄
▄▓███████████████████████████▌
▓███████████████████████████████
, ╙▓████████████████████████████▀ ▄
╓█▓▄ ╙▀▓████████████████████▀▀` ,▄██▓
╓█████▌▄, '▀▀▀▀▓▓▓▓▓▓▀▀Å╙` ▄▄▓█████▌
██████████▓▌▄ , ▄▓███████████▄
╢████████████▓ ║████▓▓███▌ ╣█████████████▓
▓█████████████ ▐█████████▀ ▓██████████████
▓█████████████ ▐█████████▄ ███████████████
▀████████████▌ ║█████████▌ ▀█████████████▌
████████████M ▓██████████ ▐█████████████⌐
▀██████████▌ ▐███████████▌ ▀███████████▌
╙▓█████▓ ▓██████████████▄ ▀███████▀
╝▓██▀ ╓▓████████████████▓ ▀▓██▀
,▄████████████████████▌,
╝▀████████████████████▓▀'
`╙▀▀▓▓███████▓▀▀╩'
▀██████████▌ ▐███████████▌ ▀███████████▌
╙▓█████▓ ▓██████████████▄ ▀███████▀
╝▓██▀ ╓▓████████████████▓ ▀▓██▀
,▄████████████████████▌,
╝▀████████████████████▓▀'
`╙▀▀▓▓███████▓▀▀╩'
EOF
;;
@@ -15234,26 +15245,26 @@ EOF
set_colors 12 12 7 12 4
read -rd '' ascii_data <<'EOF'
${c3} :${c4}:::::::::::::: ${c5}.
${c3}=#${c4}*============. ${c5}:#:
${c3}=##%${c4}+----------. ${c5}.###:
${c3}=####. ${c5}.####:
${c3}=####. ${c5}.####:
${c3}=###*. .=${c4}--------. ${c5}####:
${c3}=####. .*#+${c4}======- ${c5}####:
${c3}=###*. .*###+${c4}====- ${c5}####:
${c3}=###*. .*#####+${c4}==- ${c5}####:
${c3}=###*. .*#######+${c4}: ${c5}####:
${c3}=###*. .*#######+${c4}: ${c5}####:
${c3}=###*. .*#####+${c4}==- ${c5}####:
${c3}=###*. .*###+${c4}====- ${c5}####:
${c3}=####. .*#+${c4}======- ${c5}####:
${c3}=###*. .=${c4}--------. ${c5}.####:
${c3}=####. ${c5}.####:
${c3}=####. ${c5}.####:
${c3}=###+${c4}--------------${c5}####:
${c3}=#+${c4}=================-${c5}:
${c3}:${c4}::::::::::::::::::.
${c3}=#${c4}*============. ${c5}:#:
${c3}=##%${c4}+----------. ${c5}.###:
${c3}=####. ${c5}.####:
${c3}=####. ${c5}.####:
${c3}=###*. .=${c4}--------. ${c5}####:
${c3}=####. .*#+${c4}======- ${c5}####:
${c3}=###*. .*###+${c4}====- ${c5}####:
${c3}=###*. .*#####+${c4}==- ${c5}####:
${c3}=###*. .*#######+${c4}: ${c5}####:
${c3}=###*. .*#######+${c4}: ${c5}####:
${c3}=###*. .*#####+${c4}==- ${c5}####:
${c3}=###*. .*###+${c4}====- ${c5}####:
${c3}=####. .*#+${c4}======- ${c5}####:
${c3}=###*. .=${c4}--------. ${c5}.####:
${c3}=####. ${c5}.####:
${c3}=####. ${c5}.####:
${c3}=###+${c4}--------------${c5}####:
${c3}=#+${c4}=================-${c5}:
${c3}:${c4}::::::::::::::::::.
EOF
;;
@@ -15279,25 +15290,25 @@ EOF
set_colors 8 4
read -rd '' ascii_data <<'EOF'
${c1}
`-`
-yys+/-`
`oyyyyy: /osyyyyso+:.
/yyyyy+`+yyyyyyyyyys/.
.-yyyyys.:+//+oyyyyyyyo.
`oy/`oyyyyy/ ./syyyyy:
syyys`:yyyyyo` :yyyyy:
/yyyyo .syyyyy- .yyyyy.
yyyyy. +yyyyy/ /yyyy/
`yyyyy :yyyyys` -yyyyo
yyyyy. `syyyyy- /yyyy/
/yyyyo /yyyyy+ .yyyyy.
syyyys. -yyyyys.:yyyy:
`oyyyyyo-` `oyyyyy:.sy:
:syyyyyyso+/++`/yyyyyo``
-oyyyyyyyyyyy-.syyyys.
-/+osyyyyso.`+yyyyy/
.-/+syo`
`.
`-`
-yys+/-`
`oyyyyy: /osyyyyso+:.
/yyyyy+`+yyyyyyyyyys/.
.-yyyyys.:+//+oyyyyyyyo.
`oy/`oyyyyy/ ./syyyyy:
syyys`:yyyyyo` :yyyyy:
/yyyyo .syyyyy- .yyyyy.
yyyyy. +yyyyy/ /yyyy/
`yyyyy :yyyyys` -yyyyo
yyyyy. `syyyyy- /yyyy/
/yyyyo /yyyyy+ .yyyyy.
syyyys. -yyyyys.:yyyy:
`oyyyyyo-` `oyyyyy:.sy:
:syyyyyyso+/++`/yyyyyo``
-oyyyyyyyyyyy-.syyyys.
-/+osyyyyso.`+yyyyy/
.-/+syo`
`.
EOF
;;
@@ -15798,21 +15809,21 @@ EOF
set_colors 4 7
read -rd '' ascii_data <<'EOF'
${c1}
. .:. . .:.
.^^.!.:::. .^!?J?^
.:^.^!!!~:~^. .7??77!~~^.
.~^.!??77?!.^~: ..:^^7JJJ7~~^.
.^~.^7???7~.~~. .7??????????!
.:^:^^~^^:!^ ^: .......^!:...
.!7~.::.!.::. ~BG~ :^ ^~:
:!!~ ~. ?BBBB! ^?J!. .!~.
:!. .JBY. .Y#BBBY?~!???J7. :^^.
.. :5#B#P~P#BBP?7?55J?J7:
^P#BBBBBBBB5?7J5555J!.....
!BBBBBBGBBGJ77::Y555J?77777^
?BBBBG5JJ5PJ?!: .?Y??????77?~.
.YBGPYJ??????Y?^^^^~7?????????7?!.
.^^:..::::::::.....:::::::::::..:.
. .:. . .:.
.^^.!.:::. .^!?J?^
.:^.^!!!~:~^. .7??77!~~^.
.~^.!??77?!.^~: ..:^^7JJJ7~~^.
.^~.^7???7~.~~. .7??????????!
.:^:^^~^^:!^ ^: .......^!:...
.!7~.::.!.::. ~BG~ :^ ^~:
:!!~ ~. ?BBBB! ^?J!. .!~.
:!. .JBY. .Y#BBBY?~!???J7. :^^.
.. :5#B#P~P#BBP?7?55J?J7:
^P#BBBBBBBB5?7J5555J!.....
!BBBBBBGBBGJ77::Y555J?77777^
?BBBBG5JJ5PJ?!: .?Y??????77?~.
.YBGPYJ??????Y?^^^^~7?????????7?!.
.^^:..::::::::.....:::::::::::..:.
EOF
;;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "neowofetch",
"version": "2.0.1",
"version": "2.0.2",
"description": "Updated neofetch",
"repository": {
"type": "git",
+15 -3
View File
@@ -51,8 +51,15 @@ def edit_versions(version: str):
path = Path('hyfetch/__version__.py')
content = [f"VERSION = '{version}'" if l.startswith('VERSION = ') else l for l in path.read_text().split('\n')]
path.write_text('\n'.join(content))
# 3. Cargo.toml
print('Editing Cargo.toml...')
path = Path('Cargo.toml')
content = path.read_text()
content = re.sub(r'(?<=^version = ")[^"]+(?="$)', version, content, flags=re.MULTILINE)
path.write_text(content)
# 3. README.md
# 4. README.md
print('Editing README.md...')
path = Path('README.md')
content = path.read_text()
@@ -61,7 +68,7 @@ def edit_versions(version: str):
content = content[:changelog_i] + f'\n\n### {version}' + content[changelog_i:]
path.write_text(content)
# 4. neofetch script
# 5. neofetch script
print('Editing neofetch...')
path = Path('neofetch')
lines = path.read_text().replace("\t", " ").split('\n')
@@ -125,7 +132,12 @@ def create_release(v: str):
subprocess.check_call(['git', 'tag', f'neofetch-{NEOFETCH_NEW_VERSION}'])
i = input('Please check the commit is correct. Press y to continue or any other key to cancel.')
assert i == 'y'
if i.lower() != 'y':
print('Aborting...')
subprocess.check_call(['git', 'reset', '--hard', 'HEAD~1'])
subprocess.check_call(['git', 'tag', '-d', v])
subprocess.check_call(['git', 'tag', '-d', f'neofetch-{NEOFETCH_NEW_VERSION}'])
exit(1)
# 4. Push
print('Pushing commits...')