Compare commits

...

45 Commits

Author SHA1 Message Date
azalea f947373a23 [U] Release 2.0.4 2025-10-22 00:46:55 -04:00
azalea 5b745cde01 [U] Changelog 2025-10-22 00:29:49 -04:00
azalea 1afbd66355 [O] Specify python 3.7 to be min version 2025-10-22 12:29:29 +08:00
azalea 2fe21deea8 [F] Fix windows binary mixed with unix 2025-10-22 12:08:39 +08:00
azalea 37a19901bb [U] Update build tool to work with new builder 2025-10-22 11:56:40 +08:00
azalea 063679b72d [O] Moderize python build tool from setup-tools to hatchling 2025-10-22 11:55:07 +08:00
azalea 264cf11690 Merge branch 'master' of github.com:hykilpikonna/hyfetch 2025-10-22 11:34:09 +08:00
azalea 6280d370bc [U] Clarify notes 2025-10-22 11:30:49 +08:00
azalea 6cfc337d83 [O] Fix unused warning on non-windows 2025-10-22 11:28:52 +08:00
azalea 533de96e80 [U] Update notes 2025-10-22 11:24:49 +08:00
azalea dee984f931 [U] Release 2.0.3 2025-10-21 23:20:52 -04:00
azalea 959d42ee9c [F] Typo 2025-10-21 23:20:00 -04:00
azalea eb349aa1aa [U] Changelog 2025-10-21 23:17:56 -04:00
azalea d5751e06af [O] Shorten 2025-10-22 10:53:51 +08:00
azalea 981602b690 [O] Anyhow 2025-10-22 10:42:58 +08:00
azalea a8d752aa9f [F] Preserve preset order in codegen 2025-10-22 10:33:11 +08:00
azalea c57e8a4a49 [+] Rust codegen for presets
#451
2025-10-22 10:27:18 +08:00
azalea 67ae918ae8 [O] Fix weights
#451
2025-10-22 09:53:46 +08:00
azalea e788f93c2b [F] from __future__ import annotations 2025-10-22 08:27:50 +08:00
azalea 75a2e9d8d3 [O] Python load presets from json
#451
2025-10-22 08:24:32 +08:00
azalea 23888c0e3e Merge branch 'SHORTEN' 2025-10-21 19:52:01 +08:00
azalea e5ad9e564d Merge branch 'master' of github.com:hykilpikonna/hyfetch 2025-10-21 19:51:35 +08:00
azalea 328381b336 [+] presets.json 2025-10-21 19:50:59 +08:00
ObsoleteDev 28a181d97b [+] Transbian pride flag (#449)
* [+] Transbian flag py

* [C} Update wording on py Transbian flag

* [+] Add Transbian flag to rust

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-10-21 19:28:46 +08:00
ObsoleteDev bb514f8fd9 [+] Autism pride flag (py & rust) (#450)
Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-10-21 19:27:26 +08:00
ObsoleteDev b585ee1e26 [+] Add Transneutral & Cenelian flags (#452) 2025-10-21 19:26:14 +08:00
ObsoleteDev de2141347e Fix: Hyfetch panics on some distros that are not defined (#455) 2025-10-21 19:25:48 +08:00
Un1q32 ba88581ed4 Support iOS 1.x (#444)
* support iOS 1.x

* add comment

* reword and fix typo
2025-10-13 15:24:39 +08:00
apaz 932042b62c Swap inside fstring to double quote (#442) 2025-10-10 12:48:03 +08:00
thea f5c5e31691 [F] Fix 3-length hex codes in Python when using custom preset (#443)
[F] Fix 3-length hex codes in Python
2025-10-10 12:46:41 +08:00
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
37 changed files with 1360 additions and 2500 deletions
Generated
+325 -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.2"
version = "2.0.4"
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 }
-9
View File
@@ -1,9 +0,0 @@
include hyfetch/scripts/*
recursive-exclude tools *
recursive-exclude .github *
recursive-exclude .vscode *
exclude .gitignore
exclude .gitattributes
exclude package.json
exclude CONTRIBUTING.md
+45
View File
@@ -129,6 +129,51 @@ cargo install --git https://github.com/hykilpikonna/hyfetch
<!-- CHANGELOG STARTS HERE --->
### 2.0.4
**🔧 Building Pipeline Refactoring**
* Modernize Python building toolchain from `setup.py` to `pyproject.toml`
* Fixed a bug where Windows binaries are mistakenly built into Unix packages.
* Fixed a bug where `presets.json` was not being included in the built package.
### 2.0.3
(changelog is generated by Gemini from commit history)
This update brings a significant internal refactor to how color presets are managed, along with a new batch of pride flags and several important bug fixes.
**✨ Features & Enhancements**
* **Custom ASCII Art**: You can now specify a file path for custom ASCII art ([#429](https://github.com/hykilpikonna/hyfetch/pull/429)).
* **Hex Color Presets**: Added the ability to pass a custom comma-separated hex color string list (e.g., `#RRGGBB,#RRGGBB`) directly as a preset ([#435](https://github.com/hykilpikonna/hyfetch/pull/435)).
**🏳️‍🌈 New Flags**
* Added Transbian flag ([#449](https://github.com/hykilpikonna/hyfetch/pull/449))
* Added Autism Pride flag ([#450](https://github.com/hykilpikonna/hyfetch/pull/450))
* Added Transneutral & Cenelian flags ([#452](https://github.com/hykilpikonna/hyfetch/pull/452))
* Added Fluidflux flags ([#437](https://github.com/hykilpikonna/hyfetch/pull/437))
* Added Libragender flags ([#433](https://github.com/hykilpikonna/hyfetch/pull/433))
**🐛 Bug Fixes**
* **Crash Fix**: Fixed a panic that could occur if hyfetch was run on a distro with no defined ASCII art ([#455](https://github.com/hykilpikonna/hyfetch/pull/455)).
* **OS Support**: Added support for iOS 1.x ([#444](https://github.com/hykilpikonna/hyfetch/pull/444)) and old Apple TV models ([#438](https://github.com/hykilpikonna/hyfetch/pull/438)).
* **Python**:
* Fixed a bug where 3-length hex codes (e.g., `#FFF`) does not work properly ([#443](https://github.com/hykilpikonna/hyfetch/pull/443)).
* Fixed a string formatting issue for python 3.11 and earlier versions ([#442](https://github.com/hykilpikonna/hyfetch/pull/442)).
* Fixed a bug where extra top padding would appear above output ascii art ([#428](https://github.com/hykilpikonna/hyfetch/pull/428)).
* **Rust**:
* Fixed the logic for the pride month easter egg so it only marks itself as "displayed" during June ([#430](https://github.com/hykilpikonna/hyfetch/pull/430)).
* Fixed a build issue on Windows ([#439](https://github.com/hykilpikonna/hyfetch/pull/439)).
**🔧 Maintenance & Refactoring**
* **Preset Refactor**: Color presets are now stored in a central `presets.json` file. ([#451](https://github.com/hykilpikonna/hyfetch/issues/451)).
* **Dependency Updates**: Upgraded various Rust crates and other dependencies to their latest versions.
* **Code Cleanup**: General code shortening and readability improvements.
### 2.0.2
This is a small patch release that adds more flags and fixes some bugs from the recent Rust rewrite.
+5 -1
View File
@@ -41,10 +41,14 @@ unicode-segmentation = { workspace = true, features = [] }
which = { workspace = true, features = [] }
[build-dependencies]
indexmap = { workspace = true, features = ["std"] }
indexmap = { workspace = true, features = ["std", "serde"] }
regex = { workspace = true, features = ["perf", "std", "unicode"] }
unicode-normalization = { workspace = true, features = ["std"] }
fs_extra = "1.3.0"
serde = { workspace = true, features = ["derive", "std"] }
serde_json = { workspace = true, features = ["std"] }
anyhow = { workspace = true, features = ["std"] }
heck = "0.5.0"
[target.'cfg(windows)'.dependencies]
enable-ansi-support = { workspace = true, features = [] }
+100 -51
View File
@@ -1,11 +1,14 @@
use std::env;
use std::fmt::Write as _;
use std::fs;
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use fs_extra::dir::{CopyOptions};
use anyhow::{Context, Result};
use fs_extra::dir::CopyOptions;
use heck::ToUpperCamelCase;
use indexmap::IndexMap;
use regex::Regex;
use serde::Deserialize;
use unicode_normalization::UnicodeNormalization as _;
#[derive(Debug)]
@@ -29,35 +32,36 @@ fn anything_that_exist(paths: &[&Path]) -> Option<PathBuf> {
paths.iter().copied().find(|p| p.exists()).map(Path::to_path_buf)
}
fn main() {
fn main() -> Result<()> {
// Path hack to make file paths work in both workspace and manifest directory
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),
]).expect("couldn't find neofetch");
]).context("couldn't find neofetch")?;
let dst = o.join(file);
println!("cargo:rerun-if-changed={}", src.display());
// Copy either file or directory
if src.is_dir() {
let opt = CopyOptions { overwrite: true, copy_inside: true, ..CopyOptions::default() };
fs_extra::dir::copy(&src, &dst, &opt).expect("Failed to copy directory to OUT_DIR");
println!("copying {} to {}", src.display(), dst.display());
fs_extra::dir::copy(&src, &dst, &opt)?;
}
else { fs::copy(&src, &dst).expect("Failed to copy file to OUT_DIR"); }
else { fs::copy(&src, &dst)?; }
}
export_distros(&o.join("neofetch"), &o);
preset_codegen(&o.join("hyfetch/data/presets.json"), &o.join("presets.rs"))?;
export_distros(&o.join("neofetch"), &o)?;
Ok(())
}
fn export_distros<P>(neofetch_path: P, out_path: &Path)
where
P: AsRef<Path>,
fn export_distros(neofetch_path: &Path, out_path: &Path) -> Result<()>
{
let distros = parse_ascii_distros(neofetch_path);
let distros = parse_ascii_distros(neofetch_path)?;
let mut variants = IndexMap::with_capacity(distros.len());
for distro in &distros {
@@ -80,18 +84,13 @@ where
let mut buf = r###"
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub enum Distro {
"###
.to_owned();
"###.to_string();
for (variant, AsciiDistro { pattern, .. }) in &variants {
write!(
buf,
r###"
write!(buf, r###"
// {pattern})
{variant},
"###,
)
.unwrap();
"###)?;
}
buf.push_str(
@@ -153,15 +152,11 @@ impl Distro {
let condition = conds.join(" || ");
write!(
buf,
r###"
write!(buf, r###"
if {condition} {{
return Some(Self::{variant});
}}
"###
)
.unwrap();
"###)?;
}
buf.push_str(
@@ -176,15 +171,11 @@ impl Distro {
let quotes = "#".repeat(80);
for (variant, AsciiDistro { art, .. }) in &variants {
write!(
buf,
r###"
write!(buf, r###"
Self::{variant} => r{quotes}"
{art}
"{quotes},
"###,
)
.unwrap();
"###)?;
}
buf.push_str(
@@ -196,26 +187,23 @@ impl Distro {
"###,
);
fs::write(out_path.join("distros.rs"), buf).expect("couldn't write distros.rs");
fs::write(out_path.join("distros.rs"), buf)?;
Ok(())
}
/// Parses ascii distros from neofetch script.
fn parse_ascii_distros<P>(neofetch_path: P) -> Vec<AsciiDistro>
where
P: AsRef<Path>,
fn parse_ascii_distros(neofetch_path: &Path) -> Result<Vec<AsciiDistro>>
{
let neofetch_path = neofetch_path.as_ref();
let nf = {
let nf = fs::read_to_string(neofetch_path).expect("couldn't read neofetch script");
let nf = fs::read_to_string(neofetch_path)?;
// Get the content of "get_distro_ascii" function
let (_, nf) = nf
.split_once("get_distro_ascii() {\n")
.expect("couldn't find get_distro_ascii function");
.context("couldn't find get_distro_ascii function")?;
let (nf, _) = nf
.split_once("\n}\n")
.expect("couldn't find end of get_distro_ascii function");
.context("couldn't find end of get_distro_ascii function")?;
let mut nf = nf.replace('\t', &" ".repeat(4));
@@ -226,8 +214,8 @@ where
nf
};
let case_re = Regex::new(r"case .*? in\n").expect("couldn't compile case regex");
let eof_re = Regex::new(r"EOF[ \n]*?;;").expect("couldn't compile eof regex");
let case_re = Regex::new(r"case .*? in\n")?;
let eof_re = Regex::new(r"EOF[ \n]*?;;")?;
// Split by blocks
let mut blocks = Vec::new();
@@ -261,13 +249,74 @@ where
// for printf
let art = art.replace(r"\\", r"\");
Some(AsciiDistro {
pattern: pattern.to_owned(),
art,
})
Some(AsciiDistro { pattern: pattern.to_owned(), art })
}
blocks
.iter()
.filter_map(|block| parse_block(block))
.collect()
Ok(blocks.iter().filter_map(|block| parse_block(block)).collect())
}
// Preset parsing
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum PresetEntry {
Simple(Vec<String>),
Complex { colors: Vec<String>, weights: Option<Vec<u32>> },
}
type PresetMap = IndexMap<String, PresetEntry>;
fn preset_codegen(json_path: &Path, out_path: &Path) -> Result<()> {
// 1. Read and parse the JSON file
let json_str = fs::read_to_string(json_path)?;
let map: PresetMap = serde_json::from_str(&json_str)?;
let mut f = BufWriter::new(fs::File::create(&out_path)?);
// 2. Build the code string
let mut code_decl = String::new();
let mut code_match = String::new();
for (key, data) in map.iter() {
let colors = match data {
PresetEntry::Simple(c) => c,
PresetEntry::Complex { colors, .. } => colors,
};
let colors = colors.iter().map(|s| format!("\"{}\"", s)).collect::<Vec<_>>().join(", ");
let uck = key.to_upper_camel_case();
code_decl += &format!(r#"
#[serde(rename = "{key}")]
#[strum(serialize = "{key}")]
{uck},
"#);
let w = if let PresetEntry::Complex { weights: Some(w), .. } = data {
format!(".and_then(|c| c.with_weights(vec![{}]))", w.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", "))
} else { "".to_string() };
code_match += &format!(r#"
Preset::{uck} => ColorProfile::from_hex_colors(vec![{colors}]){w},
"#);
}
// 3. Write the static map to the generated file
writeln!(f, r#"
pub use crate::color_profile::ColorProfile;
use serde::{{Deserialize, Serialize}};
use strum::{{AsRefStr, EnumCount, EnumString, VariantArray, VariantNames}};
#[derive(Copy, Clone, Hash, Debug, AsRefStr, Deserialize, EnumCount, EnumString, Serialize, VariantArray, VariantNames)]
pub enum Preset {{
{code_decl}
}}
impl Preset {{
pub fn color_profile(&self) -> ColorProfile {{
(match self {{
{code_match}
}})
.expect("preset color profiles should be valid")
}}
}}"#)?;
f.flush()?;
Ok(())
}
+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(),
})
}
+156 -182
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,9 @@ 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::color_profile::{AssignLightness, ColorProfile};
use hyfetch::presets::{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 +66,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 +109,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 +123,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 +177,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 +242,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 +271,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 +310,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 +321,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 +367,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 +382,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 +423,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 +501,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 +518,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 +543,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 +594,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 +660,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 +675,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");
},
}
}
@@ -769,8 +723,11 @@ fn create_config(
// get distro string and convert it into the enum, neofetch friendly format, so we can check for small logos with the {distro}_small neofetch naming scheme.
let tmp_dst = get_distro_name(backend).or_else(|_| get_distro_name(Backend::Neofetch)).context("failed to get distro name")?;
let detected_dst = Some(distro.map_or_else(
|| format!("{:?}", Distro::detect(tmp_dst).unwrap()),
|d| d.to_string(),
|| {
Distro::detect(&tmp_dst)
.map_or("".to_string(), |d| format!("{:?}", d).to_lowercase())
},
|d| d.to_string().to_lowercase(),
));
// in case someone specified {distro}_small already in the --distro arg
@@ -845,16 +802,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 +921,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 +964,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 +1005,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 +1064,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!(
+232
View File
@@ -0,0 +1,232 @@
use std::iter;
use std::num::{NonZeroU8, NonZeroUsize};
use anyhow::{anyhow, Result, Context as _};
use indexmap::IndexSet;
use palette::num::ClampAssign as _;
use palette::{IntoColorMut as _, LinSrgb, Okhsl, Srgb};
use tracing::debug;
use unicode_segmentation::UnicodeSegmentation as _;
use crate::color_util::{ForegroundBackground, Lightness, ToAnsiString as _};
use crate::types::{AnsiMode, TerminalTheme};
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct ColorProfile {
pub colors: Vec<Srgb<u8>>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum AssignLightness {
Replace(Lightness),
ClampMax(Lightness),
ClampMin(Lightness),
}
impl ColorProfile {
pub fn new(colors: Vec<Srgb<u8>>) -> Self {
Self { colors }
}
pub fn from_hex_colors<S>(hex_colors: Vec<S>) -> Result<Self>
where
S: AsRef<str>,
{
let colors = hex_colors
.into_iter()
.map(|s| s.as_ref().parse())
.collect::<Result<_, _>>()
.context("failed to parse hex colors")?;
Ok(Self::new(colors))
}
/// Maps colors based on weights.
///
/// # Arguments
///
/// * `weights` - Weights of each color (`weights[i]` = how many times
/// `colors[i]` appears)
pub fn with_weights(&self, weights: Vec<u8>) -> Result<Self> {
if weights.len() != self.colors.len() {
debug!(?weights, ?self.colors, "length mismatch between `weights` and `colors`");
return Err(anyhow!(
"`weights` should have the same number of elements as `colors`"
));
}
let mut weighted_colors = Vec::new();
for (i, w) in weights.into_iter().enumerate() {
weighted_colors.extend(iter::repeat(self.colors[i]).take(usize::from(w)));
}
Ok(Self::new(weighted_colors))
}
/// Creates a new color profile, with the colors spread to the specified
/// length.
pub fn with_length(&self, length: NonZeroU8) -> Result<Self> {
let orig_len = self.colors.len();
let orig_len: NonZeroUsize = orig_len.try_into().expect("`colors` should not be empty");
let orig_len: NonZeroU8 = orig_len
.try_into()
.expect("`colors` should not have more than 255 elements");
// TODO: I believe weird things can happen because of this...
// if length < orig_len {
// unimplemented!("compressing length of color profile not implemented");
// }
let center_i = usize::from(orig_len.get() / 2);
// How many copies of each color should be displayed at least?
let repeats = length.get().div_euclid(orig_len.get());
let mut weights = vec![repeats; NonZeroUsize::from(orig_len).get()];
// How many extra spaces left?
let mut extras = length.get().rem_euclid(orig_len.get());
// If there is an odd space left, extend the center by one space
if extras % 2 == 1 {
weights[center_i] = weights[center_i].checked_add(1).unwrap();
extras = extras.checked_sub(1).unwrap();
}
// Add weight to border until there's no space left (extras must be even at this
// point)
let weights_len = weights.len();
for border_i in 0..usize::from(extras / 2) {
weights[border_i] = weights[border_i].checked_add(1).unwrap();
let border_opp = weights_len
.checked_sub(border_i)
.unwrap()
.checked_sub(1)
.unwrap();
weights[border_opp] = weights[border_opp].checked_add(1).unwrap();
}
self.with_weights(weights)
}
/// Colors a text.
///
/// # Arguments
///
/// * `foreground_background` - Whether the color is shown on the foreground
/// text or the background block
/// * `space_only` - Whether to only color spaces
pub fn color_text<S>(
&self,
txt: S,
color_mode: AnsiMode,
foreground_background: ForegroundBackground,
space_only: bool,
) -> Result<String>
where
S: AsRef<str>,
{
let txt = txt.as_ref();
let txt: Vec<&str> = txt.graphemes(true).collect();
let ColorProfile { colors } = {
let length = txt.len();
let length: NonZeroUsize = length.try_into().context("`txt` should not be empty")?;
let length: NonZeroU8 = length.try_into().with_context(|| {
format!(
"`txt` should not have more than {limit} characters",
limit = u8::MAX
)
})?;
self.with_length(length)
.with_context(|| format!("failed to spread color profile to length {length}"))?
};
let mut buf = String::new();
for (i, &gr) in txt.iter().enumerate() {
if space_only && gr != " " {
if i > 0 && txt[i.checked_sub(1).unwrap()] == " " {
buf.push_str("\x1b[39;49m");
}
buf.push_str(gr);
} else {
buf.push_str(&colors[i].to_ansi_string(color_mode, foreground_background));
buf.push_str(gr);
}
}
buf.push_str("\x1b[39;49m");
Ok(buf)
}
/// Creates a new color profile, with the colors lightened by a multiplier.
pub fn lighten(&self, multiplier: f32) -> Self {
let mut rgb_f32_colors: Vec<LinSrgb> =
self.colors.iter().map(|c| c.into_linear()).collect();
{
let okhsl_f32_colors: &mut [Okhsl] = &mut rgb_f32_colors.into_color_mut();
for okhsl_f32_color in okhsl_f32_colors {
okhsl_f32_color.lightness *= multiplier;
}
}
let rgb_u8_colors: Vec<_> = rgb_f32_colors
.into_iter()
.map(Srgb::<u8>::from_linear)
.collect();
Self {
colors: rgb_u8_colors,
}
}
/// Creates a new color profile, with the colors set to the specified
/// [`Okhsl`] lightness value.
pub fn with_lightness(&self, assign_lightness: AssignLightness) -> Self {
let mut rgb_f32_colors: Vec<LinSrgb> =
self.colors.iter().map(|c| c.into_linear()).collect();
{
let okhsl_f32_colors: &mut [Okhsl] = &mut rgb_f32_colors.into_color_mut();
for okhsl_f32_color in okhsl_f32_colors {
match assign_lightness {
AssignLightness::Replace(lightness) => {
okhsl_f32_color.lightness = lightness.into();
},
AssignLightness::ClampMax(lightness) => {
okhsl_f32_color.lightness.clamp_max_assign(lightness.into());
},
AssignLightness::ClampMin(lightness) => {
okhsl_f32_color.lightness.clamp_min_assign(lightness.into());
},
}
}
}
let rgb_u8_colors: Vec<Srgb<u8>> = rgb_f32_colors
.into_iter()
.map(Srgb::<u8>::from_linear)
.collect();
Self {
colors: rgb_u8_colors,
}
}
/// Creates a new color profile, with the colors set to the specified
/// [`Okhsl`] lightness value, adapted to the terminal theme.
pub fn with_lightness_adaptive(&self, lightness: Lightness, theme: TerminalTheme) -> Self {
match theme {
TerminalTheme::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)),
TerminalTheme::Light => self.with_lightness(AssignLightness::ClampMax(lightness)),
}
}
/// Creates another color profile with only the unique colors.
pub fn unique_colors(&self) -> Self {
let unique_colors: IndexSet<[u8; 3]> = self.colors.iter().map(|&c| c.into()).collect();
let unique_colors: Vec<Srgb<u8>> = unique_colors.into_iter().map(|c| c.into()).collect();
Self::new(unique_colors)
}
}
+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.
+1
View File
@@ -9,3 +9,4 @@ pub mod presets;
pub mod pride_month;
pub mod types;
pub mod utils;
pub mod color_profile;
+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 {
+10 -26
View File
@@ -2,7 +2,9 @@ use std::borrow::Cow;
use std::ffi::OsStr;
#[cfg(feature = "macchina")]
use std::fs;
use std::io::{self, Write as _};
use std::io::{Write as _};
#[cfg(windows)]
use std::io::{self};
use std::path::PathBuf;
use std::process::Command;
use std::sync::OnceLock;
@@ -113,16 +115,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 +283,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 +305,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))
}
+2 -900
View File
@@ -1,901 +1,3 @@
use std::iter;
use std::num::{NonZeroU8, NonZeroUsize};
#![allow(non_camel_case_types)]
use anyhow::{anyhow, Context as _, Result};
use indexmap::IndexSet;
use palette::num::ClampAssign as _;
use palette::{IntoColorMut as _, LinSrgb, Okhsl, Srgb};
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumCount, EnumString, VariantArray, VariantNames};
use tracing::debug;
use unicode_segmentation::UnicodeSegmentation as _;
use crate::color_util::{ForegroundBackground, Lightness, ToAnsiString as _};
use crate::types::{AnsiMode, TerminalTheme};
#[derive(
Copy,
Clone,
Hash,
Debug,
AsRefStr,
Deserialize,
EnumCount,
EnumString,
Serialize,
VariantArray,
VariantNames,
)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum Preset {
Rainbow,
Transgender,
Nonbinary,
Xenogender,
Agender,
Queer,
Genderfluid,
Bisexual,
Pansexual,
Polysexual,
Omnisexual,
Omniromantic,
GayMen,
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,
/// High-contrast version of pangender flag
#[serde(rename = "pangender.contrast")]
#[strum(serialize = "pangender.contrast")]
PangenderContrast,
#[serde(rename = "gendernonconforming1")]
#[strum(serialize = "gendernonconforming1")]
GenderNonconforming1,
#[serde(rename = "gendernonconforming2")]
#[strum(serialize = "gendernonconforming2")]
GenderNonconforming2,
Femboy,
Tomboy,
Gynesexual,
Androsexual,
Gendervoid,
Voidgirl,
Voidboy,
NonhumanUnity,
/// For all the dogs
Caninekin,
Plural,
Fraysexual,
Bear,
Butch,
Leather,
Otter,
Twink,
Adipophilia,
Kenochoric,
Veldian,
Solian,
Lunian,
Polyam,
Sapphic,
Androgyne,
Interprogress,
Progress,
Intersex,
OldPolyam,
EqualRights,
Drag,
Pronounfluid,
Pronounflux,
Exipronoun,
Neopronoun,
Neofluid,
Genderqueer,
Cisgender,
/// Colors from Gilbert Baker's original 1978 flag design
Baker,
/// Meme flag
Beiyang,
/// Meme flag
Burger,
/// Meme flag
#[serde(rename = "throatlozenges")]
#[strum(serialize = "throatlozenges")]
ThroatLozenges,
/// Meme flag
Band,
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct ColorProfile {
pub colors: Vec<Srgb<u8>>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum AssignLightness {
Replace(Lightness),
ClampMax(Lightness),
ClampMin(Lightness),
}
impl Preset {
pub fn color_profile(&self) -> ColorProfile {
(match self {
Self::Rainbow => ColorProfile::from_hex_colors(vec![
"#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088",
]),
Self::Transgender => ColorProfile::from_hex_colors(vec![
"#55CDFD", "#F6AAB7", "#FFFFFF", "#F6AAB7", "#55CDFD",
]),
Self::Nonbinary => {
ColorProfile::from_hex_colors(vec!["#FCF431", "#FCFCFC", "#9D59D2", "#282828"])
},
// sourced from https://commons.wikimedia.org/wiki/File:Xenogender_pride_flag.svg
Self::Xenogender => ColorProfile::from_hex_colors(vec![
"#FF6692", "#FF9A98", "#FFB883", "#FBFFA8", "#85BCFF", "#9D85FF", "#A510FF",
]),
Self::Agender => ColorProfile::from_hex_colors(vec![
"#000000", "#BABABA", "#FFFFFF", "#BAF484", "#FFFFFF", "#BABABA", "#000000",
]),
Self::Queer => ColorProfile::from_hex_colors(vec!["#B57FDD", "#FFFFFF", "#49821E"]),
Self::Genderfluid => ColorProfile::from_hex_colors(vec![
"#FE76A2", "#FFFFFF", "#BF12D7", "#000000", "#303CBE",
]),
Self::Bisexual => ColorProfile::from_hex_colors(vec!["#D60270", "#9B4F96", "#0038A8"]),
Self::Pansexual => ColorProfile::from_hex_colors(vec!["#FF1C8D", "#FFD700", "#1AB3FF"]),
Self::Polysexual => {
ColorProfile::from_hex_colors(vec!["#F714BA", "#01D66A", "#1594F6"])
},
// sourced from https://www.flagcolorcodes.com/omnisexual
Self::Omnisexual => ColorProfile::from_hex_colors(vec![
"#FE9ACE", "#FF53BF", "#200044", "#6760FE", "#8EA6FF",
]),
Self::Omniromantic => ColorProfile::from_hex_colors(vec![
"#FEC8E4", "#FDA1DB", "#89739A", "#ABA7FE", "#BFCEFF",
]),
// sourced from https://www.flagcolorcodes.com/gay-men
Self::GayMen => ColorProfile::from_hex_colors(vec![
"#078D70", "#98E8C1", "#FFFFFF", "#7BADE2", "#3D1A78",
]),
Self::Lesbian => ColorProfile::from_hex_colors(vec![
"#D62800", "#FF9B56", "#FFFFFF", "#D462A6", "#A40062",
]),
// used colorpicker to source from https://fyeahaltpride.tumblr.com/post/151704251345/could-you-guys-possibly-make-an-abrosexual-pride
Self::Abrosexual => ColorProfile::from_hex_colors(vec![
"#46D294", "#A3E9CA", "#FFFFFF", "#F78BB3", "#EE1766",
]),
Self::Asexual => {
ColorProfile::from_hex_colors(vec!["#000000", "#A4A4A4", "#FFFFFF", "#810081"])
},
Self::Aromantic => ColorProfile::from_hex_colors(vec![
"#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",
]),
// sourced from https://flag.library.lgbt/flags/aroace/
Self::Aroace2 => ColorProfile::from_hex_colors(vec![
"#000000", "#810081", "#A4A4A4", "#FFFFFF", "#A8D47A", "#3BA740",
]),
// sourced from https://flag.library.lgbt/flags/aroace/
Self::Aroace3 => ColorProfile::from_hex_colors(vec![
"#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000", "#A4A4A4", "#FFFFFF",
"#810081",
]),
// sourced from https://www.flagcolorcodes.com/greysexual
Self::Greysexual => ColorProfile::from_hex_colors(vec![
"#740194", "#AEB1AA", "#FFFFFF", "#AEB1AA", "#740194",
]),
// sourced from https://www.flagcolorcodes.com/autosexual
Self::Autosexual => ColorProfile::from_hex_colors(vec!["#99D9EA", "#7F7F7F"]),
// sourced from https://www.flagcolorcodes.com/intergender
Self::Intergender => {
ColorProfile::from_hex_colors(vec!["#900DC2", "#FFE54F", "#900DC2"])
.and_then(|c| c.with_weights(vec![2, 1, 2]))
},
// sourced from https://www.flagcolorcodes.com/greygender
Self::Greygender => ColorProfile::from_hex_colors(vec![
"#B3B3B3", "#FFFFFF", "#062383", "#FFFFFF", "#535353",
])
.and_then(|c| c.with_weights(vec![2, 1, 2, 1, 2])),
// sourced from https://www.flagcolorcodes.com/akiosexual
Self::Akiosexual => ColorProfile::from_hex_colors(vec![
"#F9485E", "#FEA06A", "#FEF44C", "#FFFFFF", "#000000",
]),
// sourced from https://www.flagcolorcodes.com/bigender
Self::Bigender => ColorProfile::from_hex_colors(vec![
"#C479A2", "#EDA5CD", "#D6C7E8", "#FFFFFF", "#D6C7E8", "#9AC7E8", "#6D82D1",
]),
// yellow sourced from https://lgbtqia.fandom.com/f/p/4400000000000041031
// other colors sourced from demiboy and demigirl flags
Self::Demigender => ColorProfile::from_hex_colors(vec![
"#7F7F7F", "#C4C4C4", "#FBFF75", "#FFFFFF", "#FBFF75", "#C4C4C4", "#7F7F7F",
]),
// sourced from https://www.flagcolorcodes.com/demiboy
Self::Demiboy => ColorProfile::from_hex_colors(vec![
"#7F7F7F", "#C4C4C4", "#9DD7EA", "#FFFFFF", "#9DD7EA", "#C4C4C4", "#7F7F7F",
]),
// sourced from https://www.flagcolorcodes.com/demigirl
Self::Demigirl => ColorProfile::from_hex_colors(vec![
"#7F7F7F", "#C4C4C4", "#FDADC8", "#FFFFFF", "#FDADC8", "#C4C4C4", "#7F7F7F",
]),
// sourced from https://www.flagcolorcodes.com/transmasculine
Self::Transmasculine => ColorProfile::from_hex_colors(vec![
"#FF8ABD", "#CDF5FE", "#9AEBFF", "#74DFFF", "#9AEBFF", "#CDF5FE", "#FF8ABD",
]),
// used colorpicker to source from https://www.deviantart.com/pride-flags/art/Trans-Woman-Transfeminine-1-543925985
// linked from https://gender.fandom.com/wiki/Transfeminine
Self::Transfeminine => ColorProfile::from_hex_colors(vec![
"#73DEFF", "#FFE2EE", "#FFB5D6", "#FF8DC0", "#FFB5D6", "#FFE2EE", "#73DEFF",
]),
// sourced from https://www.flagcolorcodes.com/genderfaun
Self::Genderfaun => ColorProfile::from_hex_colors(vec![
"#FCD689", "#FFF09B", "#FAF9CD", "#FFFFFF", "#8EDED9", "#8CACDE", "#9782EC",
]),
// sourced from https://www.flagcolorcodes.com/demifaun
Self::Demifaun => ColorProfile::from_hex_colors(vec![
"#7F7F7F", "#C6C6C6", "#FCC688", "#FFF19C", "#FFFFFF", "#8DE0D5", "#9682EC",
"#C6C6C6", "#7F7F7F",
])
.and_then(|c| c.with_weights(vec![2, 2, 1, 1, 1, 1, 1, 2, 2])),
// sourced from https://www.flagcolorcodes.com/genderfae
Self::Genderfae => ColorProfile::from_hex_colors(vec![
"#97C3A5", "#C3DEAE", "#F9FACD", "#FFFFFF", "#FCA2C4", "#DB8AE4", "#A97EDD",
]),
// used colorpicker to source form https://www.deviantart.com/pride-flags/art/Demifae-870194777
Self::Demifae => ColorProfile::from_hex_colors(vec![
"#7F7F7F", "#C5C5C5", "#97C3A4", "#C4DEAE", "#FFFFFF", "#FCA2C5", "#AB7EDF",
"#C5C5C5", "#7F7F7F",
])
.and_then(|c| c.with_weights(vec![2, 2, 1, 1, 1, 1, 1, 2, 2])),
// sourced from https://www.flagcolorcodes.com/neutrois
Self::Neutrois => ColorProfile::from_hex_colors(vec!["#FFFFFF", "#1F9F00", "#000000"]),
// sourced from https://www.flagcolorcodes.com/biromantic-alternate-2
Self::Biromantic1 => ColorProfile::from_hex_colors(vec![
"#8869A5", "#D8A7D8", "#FFFFFF", "#FDB18D", "#151638",
]),
// sourced from https://www.flagcolorcodes.com/autoromantic
Self::Autoromantic => ColorProfile::from_hex_colors(
// symbol interpreted
vec!["#99D9EA", "#3DA542", "#7F7F7F"],
)
.and_then(|c| c.with_weights(vec![2, 1, 2])),
// sourced from https://www.flagcolorcodes.com/boyflux-alternate-2
Self::Boyflux2 => ColorProfile::from_hex_colors(vec![
"#E48AE4", "#9A81B4", "#55BFAB", "#FFFFFF", "#A8A8A8", "#81D5EF", "#69ABE5",
"#5276D4",
])
.and_then(|c| c.with_weights(vec![1, 1, 1, 1, 1, 5, 5, 5])),
// sourced from https://commons.wikimedia.org/wiki/File:Girlflux_Pride_Flag.jpg
Self::Girlflux => ColorProfile::from_hex_colors(vec![
"f9e6d7", "f2526c", "bf0311", "e9c587", "bf0311", "f2526c", "f9e6d7",
]),
// sourced from https://www.deviantart.com/pride-flags/art/Genderflux-1-543925589
Self::Genderflux => ColorProfile::from_hex_colors(vec![
"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",
]),
// sourced from https://web.archive.org/web/20221002181913/https://unlabeledinfo.carrd.co/#flags
Self::Unlabeled1 => {
ColorProfile::from_hex_colors(vec!["#EAF8E4", "#FDFDFB", "#E1EFF7", "#F4E2C4"])
},
// sourced from https://web.archive.org/web/20221002181913/https://unlabeledinfo.carrd.co/#flags
Self::Unlabeled2 => ColorProfile::from_hex_colors(vec![
"#250548", "#FFFFFF", "#F7DCDA", "#EC9BEE", "#9541FA", "#7D2557",
]),
Self::Pangender => ColorProfile::from_hex_colors(vec![
"#FFF798", "#FEDDCD", "#FFEBFB", "#FFFFFF", "#FFEBFB", "#FEDDCD", "#FFF798",
]),
// high-contrast version of pangender flag
Self::PangenderContrast => ColorProfile::from_hex_colors(vec![
"#ffe87f", "#fcbaa6", "#fbc9f3", "#FFFFFF", "#fbc9f3", "#fcbaa6", "#ffe87f",
]),
Self::GenderNonconforming1 => ColorProfile::from_hex_colors(vec![
"#50284d", "#96467b", "#5c96f7", "#ffe6f7", "#5c96f7", "#96467b", "#50284d",
])
.and_then(|c| c.with_weights(vec![4, 1, 1, 1, 1, 1, 4])),
Self::GenderNonconforming2 => ColorProfile::from_hex_colors(vec![
"#50284d", "#96467b", "#5c96f7", "#ffe6f7", "#5c96f7", "#96467b", "#50284d",
]),
Self::Femboy => ColorProfile::from_hex_colors(vec![
"#d260a5", "#e4afcd", "#fefefe", "#57cef8", "#fefefe", "#e4afcd", "#d260a5",
]),
Self::Tomboy => ColorProfile::from_hex_colors(vec![
"#2f3fb9", "#613a03", "#fefefe", "#f1a9b7", "#fefefe", "#613a03", "#2f3fb9",
]),
// sourced from https://lgbtqia.fandom.com/wiki/Gynesexual
Self::Gynesexual => {
ColorProfile::from_hex_colors(vec!["#F4A9B7", "#903F2B", "#5B953B"])
},
// sourced from https://lgbtqia.fandom.com/wiki/Androsexual
Self::Androsexual => {
ColorProfile::from_hex_colors(vec!["#01CCFF", "#603524", "#B799DE"])
},
// sourced from: https://gender.fandom.com/wiki/Gendervoid
Self::Gendervoid => ColorProfile::from_hex_colors(vec![
"#081149", "#4B484B", "#000000", "#4B484B", "#081149",
]),
// sourced from: https://gender.fandom.com/wiki/Gendervoid
Self::Voidgirl => ColorProfile::from_hex_colors(vec![
"#180827", "#7A5A8B", "#E09BED", "#7A5A8B", "#180827",
]),
// sourced from: https://gender.fandom.com/wiki/Gendervoid
Self::Voidboy => ColorProfile::from_hex_colors(vec![
"#0B130C", "#547655", "#66B969", "#547655", "#0B130C",
]),
// used https://twitter.com/foxbrained/status/1667621855518236674/photo/1 as source and colorpicked
Self::NonhumanUnity => {
ColorProfile::from_hex_colors(vec!["#177B49", "#FFFFFF", "#593C90"])
},
// used https://www.tumblr.com/zombpawcoins/745062851267493888/caninekin-canine-therian-flag
Self::Caninekin => ColorProfile::from_hex_colors(vec![
"#2d2822", "#543d25", "#9c754d", "#e8dac2", "#cfad8c", "#b77b55", "#954e31",
]),
// used https://pluralpedia.org/w/Plurality#/media/File:Plural-Flag-1.jpg as source and colorpicked
Self::Plural => ColorProfile::from_hex_colors(vec![
"#2D0625", "#543475", "#7675C3", "#89C7B0", "#F3EDBD",
]),
// sampled from https://es.m.wikipedia.org/wiki/Archivo:Fraysexual_flag.jpg
Self::Fraysexual => {
ColorProfile::from_hex_colors(vec!["#226CB5", "#94E7DD", "#FFFFFF", "#636363"])
},
// sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg
Self::Bear => ColorProfile::from_hex_colors(vec![
"#623804", "#D56300", "#FEDD63", "#FEE6B8", "#FFFFFF", "#555555",
]),
// colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png
Self::Butch => ColorProfile::from_hex_colors(vec![
"#D72800", "#F17623", "#FF9C56", "#FFFDF6", "#FFCE89", "#FEAF02", "#A37000",
]),
// colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg
Self::Leather => ColorProfile::from_hex_colors(vec![
"#000000", "#252580", "#000000", "#252580", "#FFFFFF", "#252580", "#000000",
"#252580", "#000000",
]),
// colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg
Self::Otter => ColorProfile::from_hex_colors(vec![
"#263881", "#5C9DC9", "#FFFFFF", "#3A291D", "#5C9DC9", "#263881",
]),
// 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"])
},
Self::Veldian => ColorProfile::from_hex_colors(vec![
"#D182A8", "#FAF6E0", "#69ACBE", "#5D448F", "#3A113E",
]),
Self::Solian => ColorProfile::from_hex_colors(vec![
"#FFF8ED", "#FFE7A8", "#F1B870", "#A56058", "#46281E",
]),
Self::Lunian => ColorProfile::from_hex_colors(vec![
"#2F0E62", "#6F41B1", "#889FDF", "#7DDFD5", "#D2F2E2",
]),
// pulled from https://polyamproud.com/flag
Self::Polyam => ColorProfile::from_hex_colors(vec![
"#FFFFFF", "#FCBF00", "#009FE3", "#E50051", "#340C46",
]),
Self::Sapphic => ColorProfile::from_hex_colors(vec![
"#FD8BA8", "#FBF2FF", "#C76BC5", "#FDD768", "#C76BC5", "#FBF2FF", "#FD8BA8",
]),
Self::Androgyne => ColorProfile::from_hex_colors(vec!["#FE007F", "#9832FF", "#00B8E7"]),
Self::Interprogress => ColorProfile::from_hex_colors(vec![
"#FFD800", "#7902AA", "#FFFFFF", "#FFAFC8", "#74D7EE", "#613915", "#000000",
"#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088",
]),
Self::Progress => ColorProfile::from_hex_colors(vec![
"#FFFFFF", "#FFAFC8", "#74D7EE", "#613915", "#000000", "#E50000", "#FF8D00",
"#FFEE00", "#028121", "#004CFF", "#770088",
]),
Self::Intersex => ColorProfile::from_hex_colors(vec!["#FFD800", "#7902AA", "#FFD800"])
.and_then(|c| c.with_weights(vec![2, 1, 2])),
Self::OldPolyam => ColorProfile::from_hex_colors(vec![
"#0000FF", "#FF0000", "#FFFF00", "#FF0000", "#000000",
]),
Self::EqualRights => ColorProfile::from_hex_colors(vec![
"#0000FF", "#FFFF00", "#0000FF", "#FFFF00", "#0000FF",
])
.and_then(|c| c.with_weights(vec![2, 1, 2, 1, 2])),
Self::Drag => ColorProfile::from_hex_colors(vec![
"#CC67FF", "#FFFFFF", "#FFA3E3", "#FFFFFF", "#3366FF",
]),
Self::Pronounfluid => ColorProfile::from_hex_colors(vec![
"#FFB3F9", "#FFFFFF", "#D1FDCB", "#C7B0FF", "#000000", "#B8CCFF",
]),
Self::Pronounflux => ColorProfile::from_hex_colors(vec![
"#FDB3F8", "#B6CCFA", "#18DDD3", "#64FF89", "#FF7690", "#FFFFFF",
]),
Self::Exipronoun => {
ColorProfile::from_hex_colors(vec!["#1C3D34", "#FFFFFF", "#321848", "#000000"])
},
Self::Neopronoun => {
ColorProfile::from_hex_colors(vec!["#BCEC64", "#FFFFFF", "#38077A"])
},
Self::Neofluid => ColorProfile::from_hex_colors(vec![
"#FFECA0", "#FFFFFF", "#FFECA0", "#38087A", "#BCEC64",
]),
Self::Genderqueer => ColorProfile::from_hex_colors(vec![
"#B57EDC", "#FFFFFF", "#4A8123"
]),
Self::Cisgender => ColorProfile::from_hex_colors(vec![
"#D70270", "#0038A7"
]),
// used https://gilbertbaker.com/rainbow-flag-color-meanings/ as source and colorpicked
Self::Baker => ColorProfile::from_hex_colors(vec![
"#F23D9E", "#F80A24", "#F78022", "#F9E81F", "#1E972E", "#1B86BC", "#243897", "#6F0A82",
]),
Self::Beiyang => ColorProfile::from_hex_colors(vec![
"#DF1B12", "#FFC600", "#01639D", "#FFFFFF", "#000000",
]),
Self::Burger => ColorProfile::from_hex_colors(vec![
"#F3A26A", "#498701", "#FD1C13", "#7D3829", "#F3A26A",
]),
Self::ThroatLozenges => ColorProfile::from_hex_colors(vec![
"#2759DA", "#03940D", "#F5F100", "#F59B00", "#B71212",
]),
Self::Band => ColorProfile::from_hex_colors(vec![
"#2670C0", "#F5BD00", "#DC0045", "#E0608E"
]),
})
.expect("preset color profiles should be valid")
}
}
impl ColorProfile {
pub fn new(colors: Vec<Srgb<u8>>) -> Self {
Self { colors }
}
pub fn from_hex_colors<S>(hex_colors: Vec<S>) -> Result<Self>
where
S: AsRef<str>,
{
let colors = hex_colors
.into_iter()
.map(|s| s.as_ref().parse())
.collect::<Result<_, _>>()
.context("failed to parse hex colors")?;
Ok(Self::new(colors))
}
/// Maps colors based on weights.
///
/// # Arguments
///
/// * `weights` - Weights of each color (`weights[i]` = how many times
/// `colors[i]` appears)
pub fn with_weights(&self, weights: Vec<u8>) -> Result<Self> {
if weights.len() != self.colors.len() {
debug!(?weights, ?self.colors, "length mismatch between `weights` and `colors`");
return Err(anyhow!(
"`weights` should have the same number of elements as `colors`"
));
}
let mut weighted_colors = Vec::new();
for (i, w) in weights.into_iter().enumerate() {
weighted_colors.extend(iter::repeat(self.colors[i]).take(usize::from(w)));
}
Ok(Self::new(weighted_colors))
}
/// Creates a new color profile, with the colors spread to the specified
/// length.
pub fn with_length(&self, length: NonZeroU8) -> Result<Self> {
let orig_len = self.colors.len();
let orig_len: NonZeroUsize = orig_len.try_into().expect("`colors` should not be empty");
let orig_len: NonZeroU8 = orig_len
.try_into()
.expect("`colors` should not have more than 255 elements");
// TODO: I believe weird things can happen because of this...
// if length < orig_len {
// unimplemented!("compressing length of color profile not implemented");
// }
let center_i = usize::from(orig_len.get() / 2);
// How many copies of each color should be displayed at least?
let repeats = length.get().div_euclid(orig_len.get());
let mut weights = vec![repeats; NonZeroUsize::from(orig_len).get()];
// How many extra spaces left?
let mut extras = length.get().rem_euclid(orig_len.get());
// If there is an odd space left, extend the center by one space
if extras % 2 == 1 {
weights[center_i] = weights[center_i].checked_add(1).unwrap();
extras = extras.checked_sub(1).unwrap();
}
// Add weight to border until there's no space left (extras must be even at this
// point)
let weights_len = weights.len();
for border_i in 0..usize::from(extras / 2) {
weights[border_i] = weights[border_i].checked_add(1).unwrap();
let border_opp = weights_len
.checked_sub(border_i)
.unwrap()
.checked_sub(1)
.unwrap();
weights[border_opp] = weights[border_opp].checked_add(1).unwrap();
}
self.with_weights(weights)
}
/// Colors a text.
///
/// # Arguments
///
/// * `foreground_background` - Whether the color is shown on the foreground
/// text or the background block
/// * `space_only` - Whether to only color spaces
pub fn color_text<S>(
&self,
txt: S,
color_mode: AnsiMode,
foreground_background: ForegroundBackground,
space_only: bool,
) -> Result<String>
where
S: AsRef<str>,
{
let txt = txt.as_ref();
let txt: Vec<&str> = txt.graphemes(true).collect();
let ColorProfile { colors } = {
let length = txt.len();
let length: NonZeroUsize = length.try_into().context("`txt` should not be empty")?;
let length: NonZeroU8 = length.try_into().with_context(|| {
format!(
"`txt` should not have more than {limit} characters",
limit = u8::MAX
)
})?;
self.with_length(length)
.with_context(|| format!("failed to spread color profile to length {length}"))?
};
let mut buf = String::new();
for (i, &gr) in txt.iter().enumerate() {
if space_only && gr != " " {
if i > 0 && txt[i.checked_sub(1).unwrap()] == " " {
buf.push_str("\x1b[39;49m");
}
buf.push_str(gr);
} else {
buf.push_str(&colors[i].to_ansi_string(color_mode, foreground_background));
buf.push_str(gr);
}
}
buf.push_str("\x1b[39;49m");
Ok(buf)
}
/// Creates a new color profile, with the colors lightened by a multiplier.
pub fn lighten(&self, multiplier: f32) -> Self {
let mut rgb_f32_colors: Vec<LinSrgb> =
self.colors.iter().map(|c| c.into_linear()).collect();
{
let okhsl_f32_colors: &mut [Okhsl] = &mut rgb_f32_colors.into_color_mut();
for okhsl_f32_color in okhsl_f32_colors {
okhsl_f32_color.lightness *= multiplier;
}
}
let rgb_u8_colors: Vec<_> = rgb_f32_colors
.into_iter()
.map(Srgb::<u8>::from_linear)
.collect();
Self {
colors: rgb_u8_colors,
}
}
/// Creates a new color profile, with the colors set to the specified
/// [`Okhsl`] lightness value.
pub fn with_lightness(&self, assign_lightness: AssignLightness) -> Self {
let mut rgb_f32_colors: Vec<LinSrgb> =
self.colors.iter().map(|c| c.into_linear()).collect();
{
let okhsl_f32_colors: &mut [Okhsl] = &mut rgb_f32_colors.into_color_mut();
for okhsl_f32_color in okhsl_f32_colors {
match assign_lightness {
AssignLightness::Replace(lightness) => {
okhsl_f32_color.lightness = lightness.into();
},
AssignLightness::ClampMax(lightness) => {
okhsl_f32_color.lightness.clamp_max_assign(lightness.into());
},
AssignLightness::ClampMin(lightness) => {
okhsl_f32_color.lightness.clamp_min_assign(lightness.into());
},
}
}
}
let rgb_u8_colors: Vec<Srgb<u8>> = rgb_f32_colors
.into_iter()
.map(Srgb::<u8>::from_linear)
.collect();
Self {
colors: rgb_u8_colors,
}
}
/// Creates a new color profile, with the colors set to the specified
/// [`Okhsl`] lightness value, adapted to the terminal theme.
pub fn with_lightness_adaptive(&self, lightness: Lightness, theme: TerminalTheme) -> Self {
match theme {
TerminalTheme::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)),
TerminalTheme::Light => self.with_lightness(AssignLightness::ClampMax(lightness)),
}
}
/// Creates another color profile with only the unique colors.
pub fn unique_colors(&self) -> Self {
let unique_colors: IndexSet<[u8; 3]> = self.colors.iter().map(|&c| c.into()).collect();
let unique_colors: Vec<Srgb<u8>> = unique_colors.into_iter().map(|c| c.into()).collect();
Self::new(unique_colors)
}
}
include!(concat!(env!("OUT_DIR"), "/presets.rs"));
+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)?)?;
}
}
+5 -4
View File
@@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH VERSION: "1" "September 2025" "Version: 2.0.2" "User Commands"
.TH VERSION: "1" "October 2025" "Version: 2.0.4" "User Commands"
.SH NAME
Version: \- manual page for Version: 2.0.2
Version: \- manual page for Version: 2.0.4
.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
@@ -20,8 +20,9 @@ Use another config file
[default: "/home/azalea/.config/hyfetch.json"]
.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,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}
Use preset or comma\-separated color list or comma\-separated hex colors
(e.g., "#ff0000,#00ff00,#0000ff")
PRESET={rainbow,transgender,nonbinary,xenogender,agender,queer,genderfluid,bisexual,pansexual,polysexual,omnisexual,omniromantic,gay\-men,lesbian,abrosexual,asexual,aromantic,fictosexual,aroace1,aroace2,aroace3,autosexual,intergender,greygender,akiosexual,bigender,demigender,demiboy,demigirl,transmasculine,transfeminine,genderfaun,demifaun,genderfae,demifae,neutrois,biromantic1,biromantic2,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,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,caninekin,libragender,librafeminine,libramasculine,libraandrogyne,libranonbinary,fluidflux1,fluidflux2,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" "September 2025" "Neofetch 8.0.2" "User Commands"
.TH NEOFETCH "1" "October 2025" "Neofetch 8.0.4" "User Commands"
.SH NAME
Neofetch \- manual page for Neofetch 8.0.2
Neofetch \- manual page for Neofetch 8.0.4
.SH SYNOPSIS
.B neofetch
\fI\,func_name --option "value" --option "value"\/\fR
+2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import os
from .py import run_py
+3 -1
View File
@@ -1 +1,3 @@
VERSION = '2.0.2'
from __future__ import annotations
VERSION = '2.0.4'
+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] * 2, 16)
g = int(hex[1] * 2, 16)
b = int(hex[2] * 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
View File
@@ -9,6 +9,7 @@ from .types import LightDark
from .__version__ import VERSION
CONFIG_PATH = Path.home() / '.config/hyfetch.json'
SRC = Path(__file__).parent
TEST_ASCII = r"""
+227
View File
@@ -0,0 +1,227 @@
{
"rainbow": ["#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088"],
"transgender": ["#55CDFD", "#F6AAB7", "#FFFFFF", "#F6AAB7", "#55CDFD"],
"nonbinary": ["#FCF431", "#FCFCFC", "#9D59D2", "#282828"],
"xenogender": {
"colors": ["#FF6692", "#FF9A98", "#FFB883", "#FBFFA8", "#85BCFF", "#9D85FF", "#A510FF"],
"comment": "xenogender sourced from https://commons.wikimedia.org/wiki/File:Xenogender_pride_flag.svg"
},
"agender": ["#000000", "#BABABA", "#FFFFFF", "#BAF484", "#FFFFFF", "#BABABA", "#000000"],
"queer": ["#B57FDD", "#FFFFFF", "#49821E"],
"genderfluid": ["#FE76A2", "#FFFFFF", "#BF12D7", "#000000", "#303CBE"],
"bisexual": ["#D60270", "#9B4F96", "#0038A8"],
"pansexual": ["#FF1C8D", "#FFD700", "#1AB3FF"],
"polysexual": ["#F714BA", "#01D66A", "#1594F6"],
"omnisexual": {
"colors": ["#FE9ACE", "#FF53BF", "#200044", "#6760FE", "#8EA6FF"],
"comment": "omnisexual sorced from https://www.flagcolorcodes.com/omnisexual"
},
"omniromantic": ["#FEC8E4", "#FDA1DB", "#89739A", "#ABA7FE", "#BFCEFF"],
"gay-men": {
"colors": ["#078D70", "#98E8C1", "#FFFFFF", "#7BADE2", "#3D1A78"],
"comment": "gay men sourced from https://www.flagcolorcodes.com/gay-men"
},
"lesbian": ["#D62800", "#FF9B56", "#FFFFFF", "#D462A6", "#A40062"],
"abrosexual": {
"colors": ["#46D294", "#A3E9CA", "#FFFFFF", "#F78BB3", "#EE1766"],
"comment": "abrosexual used colorpicker to source from https://fyeahaltpride.tumblr.com/post/151704251345/could-you-guys-possibly-make-an-abrosexual-pride"
},
"asexual": ["#000000", "#A4A4A4", "#FFFFFF", "#810081"],
"aromantic": ["#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000"],
"fictosexual": {
"colors": ["#000000", "#C4C4C4", "#A349A5", "#C4C4C4", "#000000"],
"comment": "https://orientation.fandom.com/wiki/Fictosexual"
},
"aroace1": {
"colors": ["#E28C00", "#ECCD00", "#FFFFFF", "#62AEDC", "#203856"],
"comment": "aroace1 sourced from https://flag.library.lgbt/flags/aroace/"
},
"aroace2": ["#000000", "#810081", "#A4A4A4", "#FFFFFF", "#A8D47A", "#3BA740"],
"aroace3": ["#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000", "#A4A4A4", "#FFFFFF", "#810081"],
"autosexual": ["#99D9EA", "#7F7F7F"],
"intergender": {
"colors": ["#900DC2", "#900DC2", "#FFE54F", "#900DC2", "#900DC2"],
"comment": "todo: use weighted spacing"
},
"greygender": ["#B3B3B3", "#B3B3B3", "#FFFFFF", "#062383", "#062383", "#FFFFFF", "#535353", "#535353"],
"akiosexual": ["#F9485E", "#FEA06A", "#FEF44C", "#FFFFFF", "#000000"],
"bigender": {
"colors": ["#C479A2", "#EDA5CD", "#D6C7E8", "#FFFFFF", "#D6C7E8", "#9AC7E8", "#6D82D1"],
"comment": "bigender sourced from https://www.flagcolorcodes.com/bigender"
},
"demigender": {
"colors": ["#7F7F7F", "#C4C4C4", "#FBFF75", "#FFFFFF", "#FBFF75", "#C4C4C4", "#7F7F7F"],
"comment": "demigender yellow sourced from https://lgbtqia.fandom.com/f/p/4400000000000041031 other colors sourced from demiboy and demigirl flags"
},
"demiboy": {
"colors": ["#7F7F7F", "#C4C4C4", "#9DD7EA", "#FFFFFF", "#9DD7EA", "#C4C4C4", "#7F7F7F"],
"comment": "demiboy sourced from https://www.flagcolorcodes.com/demiboy"
},
"demigirl": {
"colors": ["#7F7F7F", "#C4C4C4", "#FDADC8", "#FFFFFF", "#FDADC8", "#C4C4C4", "#7F7F7F"],
"comment": "demigirl sourced from https://www.flagcolorcodes.com/demigirl"
},
"transmasculine": ["#FF8ABD", "#CDF5FE", "#9AEBFF", "#74DFFF", "#9AEBFF", "#CDF5FE", "#FF8ABD"],
"transfeminine": {
"colors": ["#73DEFF", "#FFE2EE", "#FFB5D6", "#FF8DC0", "#FFB5D6", "#FFE2EE", "#73DEFF"],
"comment": "transfeminine used colorpicker to source from https://www.deviantart.com/pride-flags/art/Trans-Woman-Transfeminine-1-543925985 linked from https://gender.fandom.com/wiki/Transfeminine"
},
"genderfaun": {
"colors": ["#FCD689", "#FFF09B", "#FAF9CD", "#FFFFFF", "#8EDED9", "#8CACDE", "#9782EC"],
"comment": "genderfaun sourced from https://www.flagcolorcodes.com/genderfaun"
},
"demifaun": ["#7F7F7F", "#7F7F7F", "#C6C6C6", "#C6C6C6", "#FCC688", "#FFF19C", "#FFFFFF", "#8DE0D5", "#9682EC", "#C6C6C6", "#C6C6C6", "#7F7F7F", "#7F7F7F"],
"genderfae": {
"colors": ["#97C3A5", "#C3DEAE", "#F9FACD", "#FFFFFF", "#FCA2C4", "#DB8AE4", "#A97EDD"],
"comment": "genderfae sourced from https://www.flagcolorcodes.com/genderfae"
},
"demifae": {
"colors": ["#7F7F7F", "#7F7F7F", "#C5C5C5", "#C5C5C5", "#97C3A4", "#C4DEAE", "#FFFFFF", "#FCA2C5", "#AB7EDF", "#C5C5C5", "#C5C5C5", "#7F7F7F", "#7F7F7F"],
"comment": "demifae used colorpicker to source form https://www.deviantart.com/pride-flags/art/Demifae-870194777"
},
"neutrois": ["#FFFFFF", "#1F9F00", "#000000"],
"biromantic1": ["#8869A5", "#D8A7D8", "#FFFFFF", "#FDB18D", "#151638"],
"biromantic2": ["#740194", "#AEB1AA", "#FFFFFF", "#AEB1AA", "#740194"],
"autoromantic": {
"colors": ["#99D9EA", "#99D9EA", "#3DA542", "#7F7F7F", "#7F7F7F"],
"comment": "symbol interpreted"
},
"boyflux2": {
"colors": ["#E48AE4", "#9A81B4", "#55BFAB", "#FFFFFF", "#A8A8A8", "#81D5EF", "#69ABE5", "#5276D4"],
"weights": [1, 1, 1, 1, 1, 5, 5, 5]
},
"girlflux": {
"colors": ["#F9E6D7", "#F2526C", "#BF0311", "#E9C587", "#BF0311", "#F2526C", "#F9E6D7"],
"comment": "sourced from https://commons.wikimedia.org/wiki/File:Girlflux_Pride_Flag.jpg"
},
"genderflux": {
"colors": ["#F47694", "#F2A2B9", "#CECECE", "#7CE0F7", "#3ECDF9", "#FFF48D"],
"comment": "sourced from https://www.deviantart.com/pride-flags/art/Genderflux-1-543925589"
},
"nullflux": {
"colors": ["#0B0C0E", "#A28DB9", "#E1D4EF", "#F0E6DD", "#665858"],
"comment": "https://lgbtqia.wiki/wiki/Gendernull"
},
"hypergender": ["#EFEFEF", "#FFFFFF", "#FBFF75", "#000000", "#FBFF75", "#FFFFFF", "#EFEFEF"],
"hyperboy": ["#EFEFEF", "#FFFFFF", "#74D7FE", "#000000", "#74D7FE", "#FFFFFF", "#EFEFEF"],
"hypergirl": ["#EFEFEF", "#FFFFFF", "#FC76D3", "#000000", "#FC76D3", "#FFFFFF", "#EFEFEF"],
"hyperandrogyne": ["#EFEFEF", "#FFFFFF", "#BB83FF", "#000000", "#BB83FF", "#FFFFFF", "#EFEFEF"],
"hyperneutrois": ["#EFEFEF", "#FFFFFF", "#BAFA74", "#000000", "#BAFA74", "#FFFFFF", "#EFEFEF"],
"finsexual": ["#B18EDF", "#D7B1E2", "#F7CDE9", "#F39FCE", "#EA7BB3"],
"unlabeled1": ["#EAF8E4", "#FDFDFB", "#E1EFF7", "#F4E2C4"],
"unlabeled2": ["#250548", "#FFFFFF", "#F7DCDA", "#EC9BEE", "#9541FA", "#7D2557"],
"pangender": ["#FFF798", "#FEDDCD", "#FFEBFB", "#FFFFFF", "#FFEBFB", "#FEDDCD", "#FFF798"],
"pangender.contrast": ["#FFE87F", "#FCBAA6", "#FBC9F3", "#FFFFFF", "#FBC9F3", "#FCBAA6", "#FFE87F"],
"gendernonconforming1": {
"colors": ["#50284D", "#96467B", "#5C96F7", "#FFE6F7", "#5C96F7", "#96467B", "#50284D"],
"weights": [4, 1, 1, 1, 1, 1, 4]
},
"gendernonconforming2": ["#50284D", "#96467B", "#5C96F7", "#FFE6F7", "#5C96F7", "#96467B", "#50284D"],
"femboy": ["#D260A5", "#E4AFCD", "#FEFEFE", "#57CEF8", "#FEFEFE", "#E4AFCD", "#D260A5"],
"tomboy": ["#2F3FB9", "#613A03", "#FEFEFE", "#F1A9B7", "#FEFEFE", "#613A03", "#2F3FB9"],
"gynesexual": ["#F4A9B7", "#903F2B", "#5B953B"],
"androsexual": ["#01CCFF", "#603524", "#B799DE"],
"gendervoid": {
"colors": ["#081149", "#4B484B", "#000000", "#4B484B", "#081149"],
"comment": "gendervoid and related flags sourced from: https://gender.fandom.com/wiki/Gendervoid"
},
"voidgirl": ["#180827", "#7A5A8B", "#E09BED", "#7A5A8B", "#180827"],
"voidboy": ["#0B130C", "#547655", "#66B969", "#547655", "#0B130C"],
"nonhuman-unity": {
"colors": ["#177B49", "#FFFFFF", "#593C90"],
"comment": "used https://twitter.com/foxbrained/status/1667621855518236674/photo/1 as source and colorpicked"
},
"plural": {
"colors": ["#2D0625", "#543475", "#7675C3", "#89C7B0", "#F3EDBD"],
"comment": "used https://pluralpedia.org/w/Plurality#/media/File:Plural-Flag-1.jpg as source and colorpicked"
},
"fraysexual": {
"colors": ["#226CB5", "#94E7DD", "#FFFFFF", "#636363"],
"comment": "sampled from https://es.m.wikipedia.org/wiki/Archivo:Fraysexual_flag.jpg"
},
"bear": {
"colors": ["#623804", "#D56300", "#FEDD63", "#FEE6B8", "#FFFFFF", "#555555"],
"comment": "sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg"
},
"butch": {
"colors": ["#D72800", "#F17623", "#FF9C56", "#FFFDF6", "#FFCE89", "#FEAF02", "#A37000"],
"comment": "colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png"
},
"leather": {
"colors": ["#000000", "#252580", "#000000", "#252580", "#FFFFFF", "#252580", "#000000", "#252580", "#000000"],
"comment": "colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg"
},
"otter": {
"colors": ["#263881", "#5C9DC9", "#FFFFFF", "#3A291D", "#5C9DC9", "#263881"],
"comment": "colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg"
},
"twink": {
"colors": ["#FFB2FF", "#FFFFFF", "#FFFF81"],
"comment": "colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg"
},
"adipophilia": {
"colors": ["#000000", "#E16180", "#FFF9BE", "#603E41", "#000000"],
"comment": "https://en.wikipedia.org/wiki/File:FatFetishFlag.png"
},
"kenochoric": ["#000000", "#2E1569", "#824DB7", "#C7A1D6"],
"veldian": ["#D182A8", "#FAF6E0", "#69ACBE", "#5D448F", "#3A113E"],
"solian": ["#FFF8ED", "#FFE7A8", "#F1B870", "#A56058", "#46281E"],
"lunian": ["#2F0E62", "#6F41B1", "#889FDF", "#7DDFD5", "#D2F2E2"],
"polyam": {
"colors": ["#FFFFFF", "#FCBF00", "#009FE3", "#E50051", "#340C46"],
"comment": "polyamorous flag colors pulled from https://polyamproud.com/flag"
},
"sapphic": ["#FD8BA8", "#FBF2FF", "#C76BC5", "#FDD768", "#C76BC5", "#FBF2FF", "#FD8BA8"],
"androgyne": ["#FE007F", "#9832FF", "#00B8E7"],
"interprogress": ["#FFD800", "#7902AA", "#FFFFFF", "#FFAFC8", "#74D7EE", "#613915", "#000000", "#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088"],
"progress": ["#FFFFFF", "#FFAFC8", "#74D7EE", "#613915", "#000000", "#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088"],
"intersex": ["#FFD800", "#FFD800", "#7902AA", "#FFD800", "#FFD800"],
"old-polyam": ["#0000FF", "#FF0000", "#FFFF00", "#FF0000", "#000000"],
"equal-rights": ["#0000FF", "#0000FF", "#FFFF00", "#0000FF", "#0000FF", "#FFFF00", "#0000FF", "#0000FF"],
"drag": ["#CC67FF", "#FFFFFF", "#FFA3E3", "#FFFFFF", "#3366FF"],
"pronounfluid": ["#FFB3F9", "#FFFFFF", "#D1FDCB", "#C7B0FF", "#000000", "#B8CCFF"],
"pronounflux": ["#FDB3F8", "#B6CCFA", "#18DDD3", "#64FF89", "#FF7690", "#FFFFFF"],
"exipronoun": ["#1C3D34", "#FFFFFF", "#321848", "#000000"],
"neopronoun": ["#BCEC64", "#FFFFFF", "#38077A"],
"neofluid": ["#FFECA0", "#FFFFFF", "#FFECA0", "#38087A", "#BCEC64"],
"genderqueer": ["#B57EDC", "#B57EDC", "#FFFFFF", "#FFFFFF", "#4A8123", "#4A8123"],
"cisgender": ["#D70270", "#0038A7"],
"baker": {
"colors": ["#F23D9E", "#F80A24", "#F78022", "#F9E81F", "#1E972E", "#1B86BC", "#243897", "#6F0A82"],
"comment": "colors from Gilbert Baker's original 1978 flag design used https://gilbertbaker.com/rainbow-flag-color-meanings/ as source and colorpicked"
},
"caninekin": {
"colors": ["#2D2822", "#543D25", "#9C754D", "#E8DAC2", "#CFAD8C", "#B77B55", "#954E31"],
"comment": "this is 4 all the dogs, from zombpawcoins on tumblr!"
},
"libragender": {
"colors": ["#000000", "#808080", "#92D8E9", "#FFF544", "#FFB0CA", "#808080", "#000000"],
"comment": "Sourced from https://lgbtqia.wiki/wiki/Libragender"
},
"librafeminine": {
"colors": ["#000000", "#A3A3A3", "#FFFFFF", "#C6568F", "#FFFFFF", "#A3A3A3", "#000000"],
"comment": "Sourced from https://lgbtqia.wiki/wiki/Librafeminine"
},
"libramasculine": {
"colors": ["#000000", "#A3A3A3", "#FFFFFF", "#56C5C5", "#FFFFFF", "#A3A3A3", "#000000"],
"comment": "Sourced from https://lgbtqia.wiki/wiki/Libramasculine"
},
"libraandrogyne": {
"colors": ["#000000", "#A3A3A3", "#FFFFFF", "#9186B1", "#FFFFFF", "#A3A3A3", "#000000"],
"comment": "Sourced from https://lgbtqia.wiki/wiki/Librandrogyne"
},
"libranonbinary": {
"colors": ["#000000", "#A3A3A3", "#FFFFFF", "#FFF987", "#FFFFFF", "#A3A3A3", "#000000"],
"comment": "Sourced from https://lgbtqia.wiki/wiki/Libranonbinary"
},
"fluidflux1": {
"colors": ["#FF115F", "#A34AA3", "#00A4E7", "#FFDF00", "#000000", "#FFED71", "#85DAFF", "#DBADDA", "#FE8DB1"],
"comment": "Sourced from https://gender.fandom.com/wiki/Fluidflux?file=FC90B24D-CA36-4FE2-A752-C9ABFC65E332.jpeg"
},
"fluidflux2": ["#C6D1D2", "#F47B9D", "#F09F9B", "#E3F09E", "#75EEEA", "#52D2ED", "#C6D1D2"],
"beiyang": ["#DF1B12", "#FFC600", "#01639D", "#FFFFFF", "#000000"],
"burger": ["#F3A26A", "#498701", "#FD1C13", "#7D3829", "#F3A26A"],
"throatlozenges": ["#2759DA", "#03940D", "#F5F100", "#F59B00", "#B71212"],
"band": ["#2670C0", "#F5BD00", "#DC0045", "#E0608E"]
}
+6 -5
View File
@@ -1,8 +1,9 @@
import json
from pathlib import Path
from __future__ import annotations
from hyfetch.constants import CACHE_PATH
from hyfetch.neofetch_util import get_distro_name
import json
from .constants import CACHE_PATH, SRC
from .neofetch_util import get_distro_name
def get_font_logo() -> str:
@@ -10,7 +11,7 @@ def get_font_logo() -> str:
if cache.exists():
return cache.read_text('utf-8')
font_logos: dict[str, str] = json.loads((Path(__file__).parent / 'data/font_logos.json').read_text('utf-8'))
font_logos: dict[str, str] = json.loads((SRC / 'data/font_logos.json').read_text('utf-8'))
font_logos = {k.lower(): v for k, v in font_logos.items()}
# Get the distro
+66 -5
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')
@@ -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):
+1 -2
View File
@@ -14,14 +14,13 @@ from tempfile import TemporaryDirectory
from typing import Iterable
from .color_util import color, printc
from .constants import GLOBAL_CFG, IS_WINDOWS
from .constants import GLOBAL_CFG, IS_WINDOWS, SRC
from .distros import distro_detector
from .presets import ColorProfile
from .serializer import from_dict
from .types import BackendLiteral, ColorAlignMode
RE_NEOFETCH_COLOR = re.compile('\\${c[0-9]}')
SRC = Path(__file__).parent
def literal_input(prompt: str, options: Iterable[str], default: str, show_ops: bool = True) -> str:
+14 -847
View File
@@ -1,9 +1,10 @@
from __future__ import annotations
import json
from typing import Iterable
from .color_util import RGB
from .constants import GLOBAL_CFG
from .constants import GLOBAL_CFG, SRC
from .types import LightDark, ColorSpacing
@@ -21,6 +22,16 @@ class ColorProfile:
colors: list[RGB]
spacing: ColorSpacing = 'equal'
@staticmethod
def from_json(data: list | dict) -> 'ColorProfile':
if isinstance(data, list):
return ColorProfile(data)
else:
pf = ColorProfile(data['colors'])
if 'weights' in data:
pf = ColorProfile(pf.with_weights(data['weights']))
return pf
def __init__(self, colors: list[str] | list[RGB]):
if isinstance(colors[0], str):
self.raw = colors
@@ -165,850 +176,6 @@ class ColorProfile:
PRESETS: dict[str, ColorProfile] = {
'rainbow': ColorProfile([
'#E50000',
'#FF8D00',
'#FFEE00',
'#028121',
'#004CFF',
'#770088'
]),
'transgender': ColorProfile([
'#55CDFD',
'#F6AAB7',
'#FFFFFF',
'#F6AAB7',
'#55CDFD'
]),
'nonbinary': ColorProfile([
'#FCF431',
'#FCFCFC',
'#9D59D2',
'#282828'
]),
# xenogender sourced from https://commons.wikimedia.org/wiki/File:Xenogender_pride_flag.svg
'xenogender': ColorProfile([
'#FF6692',
'#FF9A98',
'#FFB883',
'#FBFFA8',
'#85BCFF',
'#9D85FF',
'#A510FF'
]),
'agender': ColorProfile([
'#000000',
'#BABABA',
'#FFFFFF',
'#BAF484',
'#FFFFFF',
'#BABABA',
'#000000'
]),
'queer': ColorProfile([
'#B57FDD',
'#FFFFFF',
'#49821E'
]),
'genderfluid': ColorProfile([
'#FE76A2',
'#FFFFFF',
'#BF12D7',
'#000000',
'#303CBE'
]),
'bisexual': ColorProfile([
'#D60270',
'#9B4F96',
'#0038A8'
]),
'pansexual': ColorProfile([
'#FF1C8D',
'#FFD700',
'#1AB3FF'
]),
'polysexual': ColorProfile([
'#F714BA',
'#01D66A',
'#1594F6',
]),
# omnisexual sorced from https://www.flagcolorcodes.com/omnisexual
'omnisexual': ColorProfile([
'#FE9ACE',
'#FF53BF',
'#200044',
'#6760FE',
'#8EA6FF',
]),
'omniromantic': ColorProfile([
'#FEC8E4',
'#FDA1DB',
'#89739A',
'#ABA7FE',
'#BFCEFF',
]),
# gay men sourced from https://www.flagcolorcodes.com/gay-men
'gay-men': ColorProfile([
'#078D70',
'#98E8C1',
'#FFFFFF',
'#7BADE2',
'#3D1A78'
]),
'lesbian': ColorProfile([
'#D62800',
'#FF9B56',
'#FFFFFF',
'#D462A6',
'#A40062'
]),
# abrosexual used colorpicker to source from
# https://fyeahaltpride.tumblr.com/post/151704251345/could-you-guys-possibly-make-an-abrosexual-pride
'abrosexual': ColorProfile([
'#46D294',
'#A3E9CA',
'#FFFFFF',
'#F78BB3',
'#EE1766',
]),
'asexual': ColorProfile([
'#000000',
'#A4A4A4',
'#FFFFFF',
'#810081'
]),
'aromantic': ColorProfile([
'#3BA740',
'#A8D47A',
'#FFFFFF',
'#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([
'#E28C00',
'#ECCD00',
'#FFFFFF',
'#62AEDC',
'#203856'
]),
'aroace2': ColorProfile([
'#000000',
'#810081',
'#A4A4A4',
'#FFFFFF',
'#A8D47A',
'#3BA740'
]),
'aroace3': ColorProfile([
'#3BA740',
'#A8D47A',
'#FFFFFF',
'#ABABAB',
'#000000',
'#A4A4A4',
'#FFFFFF',
'#810081'
]),
# below sourced from https://www.flagcolorcodes.com/flags/pride
# goto f"https://www.flagcolorcodes.com/{preset}" for info
# todo: sane sorting
'autosexual': ColorProfile([
'#99D9EA',
'#7F7F7F'
]),
'intergender': ColorProfile([
# todo: use weighted spacing
'#900DC2',
'#900DC2',
'#FFE54F',
'#900DC2',
'#900DC2',
]),
'greygender': ColorProfile([
'#B3B3B3',
'#B3B3B3',
'#FFFFFF',
'#062383',
'#062383',
'#FFFFFF',
'#535353',
'#535353',
]),
'akiosexual': ColorProfile([
'#F9485E',
'#FEA06A',
'#FEF44C',
'#FFFFFF',
'#000000',
]),
# bigender sourced from https://www.flagcolorcodes.com/bigender
'bigender': ColorProfile([
'#C479A2',
'#EDA5CD',
'#D6C7E8',
'#FFFFFF',
'#D6C7E8',
'#9AC7E8',
'#6D82D1',
]),
# demigender yellow sourced from https://lgbtqia.fandom.com/f/p/4400000000000041031
# other colors sourced from demiboy and demigirl flags
'demigender': ColorProfile([
'#7F7F7F',
'#C4C4C4',
'#FBFF75',
'#FFFFFF',
'#FBFF75',
'#C4C4C4',
'#7F7F7F',
]),
# demiboy sourced from https://www.flagcolorcodes.com/demiboy
'demiboy': ColorProfile([
'#7F7F7F',
'#C4C4C4',
'#9DD7EA',
'#FFFFFF',
'#9DD7EA',
'#C4C4C4',
'#7F7F7F',
]),
# demigirl sourced from https://www.flagcolorcodes.com/demigirl
'demigirl': ColorProfile([
'#7F7F7F',
'#C4C4C4',
'#FDADC8',
'#FFFFFF',
'#FDADC8',
'#C4C4C4',
'#7F7F7F',
]),
'transmasculine': ColorProfile([
'#FF8ABD',
'#CDF5FE',
'#9AEBFF',
'#74DFFF',
'#9AEBFF',
'#CDF5FE',
'#FF8ABD',
]),
# transfeminine used colorpicker to source from https://www.deviantart.com/pride-flags/art/Trans-Woman-Transfeminine-1-543925985
# linked from https://gender.fandom.com/wiki/Transfeminine
'transfeminine': ColorProfile([
'#73DEFF',
'#FFE2EE',
'#FFB5D6',
'#FF8DC0',
'#FFB5D6',
'#FFE2EE',
'#73DEFF',
]),
# genderfaun sourced from https://www.flagcolorcodes.com/genderfaun
'genderfaun': ColorProfile([
'#FCD689',
'#FFF09B',
'#FAF9CD',
'#FFFFFF',
'#8EDED9',
'#8CACDE',
'#9782EC',
]),
'demifaun': ColorProfile([
'#7F7F7F',
'#7F7F7F',
'#C6C6C6',
'#C6C6C6',
'#FCC688',
'#FFF19C',
'#FFFFFF',
'#8DE0D5',
'#9682EC',
'#C6C6C6',
'#C6C6C6',
'#7F7F7F',
'#7F7F7F',
]),
# genderfae sourced from https://www.flagcolorcodes.com/genderfae
'genderfae': ColorProfile([
'#97C3A5',
'#C3DEAE',
'#F9FACD',
'#FFFFFF',
'#FCA2C4',
'#DB8AE4',
'#A97EDD',
]),
# demifae used colorpicker to source form https://www.deviantart.com/pride-flags/art/Demifae-870194777
'demifae': ColorProfile([
'#7F7F7F',
'#7F7F7F',
'#C5C5C5',
'#C5C5C5',
'#97C3A4',
'#C4DEAE',
'#FFFFFF',
'#FCA2C5',
'#AB7EDF',
'#C5C5C5',
'#C5C5C5',
'#7F7F7F',
'#7F7F7F',
]),
'neutrois': ColorProfile([
'#FFFFFF',
'#1F9F00',
'#000000'
]),
'biromantic1': ColorProfile([
'#8869A5',
'#D8A7D8',
'#FFFFFF',
'#FDB18D',
'#151638',
]),
'biromantic2': ColorProfile([
'#740194',
'#AEB1AA',
'#FFFFFF',
'#AEB1AA',
'#740194',
]),
'autoromantic': ColorProfile([ # symbol interpreted
'#99D9EA',
'#99D9EA',
'#3DA542',
'#7F7F7F',
'#7F7F7F',
]),
# i didn't expect this one to work. cool!
'boyflux2': ColorProfile(ColorProfile([
'#E48AE4',
'#9A81B4',
'#55BFAB',
'#FFFFFF',
'#A8A8A8',
'#81D5EF',
'#69ABE5',
'#5276D4',
]).with_weights([1, 1, 1, 1, 1, 5, 5, 5])),
# sourced from https://commons.wikimedia.org/wiki/File:Girlflux_Pride_Flag.jpg
"girlflux": ColorProfile([
"f9e6d7",
"f2526c",
"bf0311",
"e9c587",
"bf0311",
"f2526c",
"f9e6d7",
]),
# sourced from https://www.deviantart.com/pride-flags/art/Genderflux-1-543925589
"genderflux": ColorProfile([
"f47694",
"f2a2b9",
"cecece",
"7ce0f7",
"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",
"#D7B1E2",
"#F7CDE9",
"#F39FCE",
"#EA7BB3",
]),
'unlabeled1': ColorProfile([
'#EAF8E4',
'#FDFDFB',
'#E1EFF7',
'#F4E2C4'
]),
'unlabeled2': ColorProfile([
'#250548',
'#FFFFFF',
'#F7DCDA',
'#EC9BEE',
'#9541FA',
'#7D2557'
]),
'pangender': ColorProfile([
'#FFF798',
'#FEDDCD',
'#FFEBFB',
'#FFFFFF',
'#FFEBFB',
'#FEDDCD',
'#FFF798',
]),
'pangender.contrast': ColorProfile([
'#ffe87f',
'#fcbaa6',
'#fbc9f3',
'#FFFFFF',
'#fbc9f3',
'#fcbaa6',
'#ffe87f',
]),
'gendernonconforming1': ColorProfile(
ColorProfile([
'#50284d',
'#96467b',
'#5c96f7',
'#ffe6f7',
'#5c96f7',
'#96467b',
'#50284d'
]).with_weights([
4, 1, 1, 1, 1, 1, 4
])
),
'gendernonconforming2': ColorProfile([
'#50284d',
'#96467b',
'#5c96f7',
'#ffe6f7',
'#5c96f7',
'#96467b',
'#50284d'
]),
'femboy': ColorProfile([
"#d260a5",
"#e4afcd",
"#fefefe",
"#57cef8",
"#fefefe",
"#e4afcd",
"#d260a5"
]),
'tomboy': ColorProfile([
"#2f3fb9",
"#613a03",
"#fefefe",
"#f1a9b7",
"#fefefe",
"#613a03",
"#2f3fb9"
]),
'gynesexual': ColorProfile([
"#F4A9B7",
"#903F2B",
"#5B953B",
]),
'androsexual': ColorProfile([
"#01CCFF",
"#603524",
"#B799DE",
]),
# gendervoid and related flags sourced from: https://gender.fandom.com/wiki/Gendervoid
'gendervoid': ColorProfile([
"#081149",
"#4B484B",
"#000000",
"#4B484B",
"#081149"
]),
'voidgirl': ColorProfile([
"#180827",
"#7A5A8B",
"#E09BED",
"#7A5A8B",
"#180827"
]),
'voidboy': ColorProfile([
"#0B130C",
"#547655",
"#66B969",
"#547655",
"#0B130C"
]),
# used https://twitter.com/foxbrained/status/1667621855518236674/photo/1 as source and colorpicked
'nonhuman-unity': ColorProfile([
"#177B49",
"#FFFFFF",
"#593C90"
]),
# used https://pluralpedia.org/w/Plurality#/media/File:Plural-Flag-1.jpg as source and colorpicked
'plural': ColorProfile([
"#2D0625",
"#543475",
"#7675C3",
"#89C7B0",
"#F3EDBD",
]),
# sampled from https://es.m.wikipedia.org/wiki/Archivo:Fraysexual_flag.jpg
'fraysexual': ColorProfile([
'#226CB5',
'#94E7DD',
'#FFFFFF',
'#636363',
]),
# Queer Subcultures
# sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg
'bear': ColorProfile([
'#623804',
'#D56300',
'#FEDD63',
'#FEE6B8',
'#FFFFFF',
'#555555',
]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png
'butch': ColorProfile([
'#D72800',
'#F17623',
'#FF9C56',
'#FFFDF6',
'#FFCE89',
'#FEAF02',
'#A37000',
]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg
'leather': ColorProfile([
'#000000',
'#252580',
'#000000',
'#252580',
'#FFFFFF',
'#252580',
'#000000',
'#252580',
'#000000',
]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg
'otter': ColorProfile([
'#263881',
'#5C9DC9',
'#FFFFFF',
'#3A291D',
'#5C9DC9',
'#263881',
]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg
'twink': ColorProfile([
'#FFB2FF',
'#FFFFFF',
'#FFFF81',
]),
# https://en.wikipedia.org/wiki/File:FatFetishFlag.png
'adipophilia': ColorProfile(["#000000", "#E16180", "#FFF9BE", "#603E41", "#000000"]),
'kenochoric': ColorProfile([
'#000000',
'#2E1569',
'#824DB7',
'#C7A1D6',
]),
'veldian': ColorProfile([
'#D182A8',
'#FAF6E0',
'#69ACBE',
'#5D448F',
'#3A113E',
]),
'solian': ColorProfile([
'#FFF8ED',
'#FFE7A8',
'#F1B870',
'#A56058',
'#46281E',
]),
'lunian': ColorProfile([
'#2F0E62',
'#6F41B1',
'#889FDF',
'#7DDFD5',
'#D2F2E2',
]),
# Start of Extras by Jaida Corvera
# polyamorous flag colors pulled from https://polyamproud.com/flag
'polyam': ColorProfile([
"#FFFFFF",
"#FCBF00",
"#009FE3",
"#E50051",
"#340C46",
]),
'sapphic': ColorProfile([
"#FD8BA8",
"#FBF2FF",
"#C76BC5",
"#FDD768",
"#C76BC5",
"#FBF2FF",
"#FD8BA8"
]),
'androgyne': ColorProfile([
"#FE007F",
"#9832FF",
"#00B8E7",
]),
'interprogress': ColorProfile([
"#FFD800",
"#7902AA",
"#FFFFFF",
"#FFAFC8",
"#74D7EE",
"#613915",
"#000000",
'#E50000',
'#FF8D00',
'#FFEE00',
'#028121',
'#004CFF',
'#770088'
]),
'progress': ColorProfile([
"#FFFFFF",
"#FFAFC8",
"#74D7EE",
"#613915",
"#000000",
'#E50000',
'#FF8D00',
'#FFEE00',
'#028121',
'#004CFF',
'#770088'
]),
'intersex': ColorProfile([
"#FFD800",
"#FFD800",
"#7902AA",
"#FFD800",
"#FFD800"
]),
'old-polyam': ColorProfile([
"#0000FF",
"#FF0000",
"#FFFF00",
"#FF0000",
"#000000"
]),
'equal-rights': ColorProfile([
"#0000FF",
"#0000FF",
"#FFFF00",
"#0000FF",
"#0000FF",
"#FFFF00",
"#0000FF",
"#0000FF"
]),
'drag': ColorProfile([
"#CC67FF",
"#FFFFFF",
"#FFA3E3",
"#FFFFFF",
"#3366FF"
]),
# Pronoun Flags
'pronounfluid': ColorProfile([
"#ffb3f9",
"#ffffff",
"#d1fdcb",
"#c7b0ff",
"#000000",
"#b8ccff"
]),
'pronounflux': ColorProfile([
"#fdb3f8",
"#b6ccfa",
"#18ddd3",
"#64ff89",
"#ff7690",
"#ffffff"
]),
'exipronoun': ColorProfile([
"#1c3d34",
"#ffffff",
"#321848",
"#000000"
]),
'neopronoun': ColorProfile([
"#bcec64",
"#ffffff",
"#38077a"
]),
'neofluid': ColorProfile([
"#ffeca0",
"#ffffff",
"#ffeca0",
"#38087a",
"#bcec64"
]),
'genderqueer': ColorProfile([
"#b57edc",
"#b57edc",
"#ffffff",
"#ffffff",
"#4a8123",
"#4a8123"
]),
'cisgender': ColorProfile([
"#D70270",
"#0038A7"
]),
# colors from Gilbert Baker's original 1978 flag design
# used https://gilbertbaker.com/rainbow-flag-color-meanings/ as source and colorpicked
'baker': ColorProfile([
'#F23D9E',
'#F80A24',
'#F78022',
'#F9E81F',
'#1E972E',
'#1B86BC',
'#243897',
'#6F0A82',
]),
# this is 4 all the dogs, from zombpawcoins on tumblr!
'caninekin': ColorProfile([
'#2d2822',
'#543d25',
'#9c754d',
'#e8dac2',
'#cfad8c',
'#b77b55',
'#954e31'
]),
# Meme flags
'beiyang': ColorProfile([
'#DF1B12',
'#FFC600',
'#01639D',
'#FFFFFF',
'#000000',
]),
'burger': ColorProfile([
'#F3A26A',
'#498701',
'#FD1C13',
'#7D3829',
'#F3A26A',
]),
'throatlozenges': ColorProfile([
"#2759DA",
"#03940D",
"#F5F100",
"#F59B00",
"#B71212"
]),
'band': ColorProfile([
"#2670c0",
"#f5bd00",
"#dc0045",
"#e0608e"
]),
k: ColorProfile.from_json(v)
for k, v in json.loads((SRC / 'data/presets.json').read_text('utf-8')).items()
}
+6 -5
View File
@@ -1,11 +1,12 @@
from __future__ import annotations
import math
from time import sleep
from hyfetch import presets
from hyfetch.color_util import RGB, color, printc
from hyfetch.constants import IS_WINDOWS
from hyfetch.neofetch_util import term_size
from hyfetch.presets import PRESETS
from .color_util import RGB, color, printc
from .constants import IS_WINDOWS
from .neofetch_util import term_size
from .presets import PRESETS
def key_pressed():
+3
View File
@@ -1,6 +1,9 @@
from __future__ import annotations
from . import main
from .color_util import printc
def run_py():
try:
main.run()
+4 -3
View File
@@ -1,17 +1,18 @@
from __future__ import annotations
import os
import platform
import subprocess
import sys
from pathlib import Path
from .color_util import printc
from .constants import SRC
from .py import run_py
def run_rust():
# Find the rust executable
pd = Path(__file__).parent.joinpath('rust')
pd = pd.joinpath('hyfetch.exe' if platform.system() == 'Windows' else 'hyfetch')
pd = SRC / 'rust' / ('hyfetch.exe' if platform.system() == 'Windows' else 'hyfetch')
if not pd.exists():
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'
+19 -3
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.2
version=8.0.4
# Fallback to a value of '5' for shells which support bash
# but do not set the 'BASH_' shell variables (osh).
@@ -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=$_
@@ -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="$_"
;;
@@ -6385,6 +6396,11 @@ cache_uname() {
ProductVersion) osx_version=${sw_vers[i+1]} ;;
ProductBuildVersion) osx_build=${sw_vers[i+1]} ;;
esac
# before iOS 2, iOS didn't internally distinguish between itself and macOS,
# so we manually set the OS type if the version is 1.x
case $osx_version in
1.*) darwin_name="iPhone OS" ;;
esac
}
fi
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "neowofetch",
"version": "2.0.2",
"version": "2.0.4",
"description": "Updated neofetch",
"repository": {
"type": "git",
+52
View File
@@ -0,0 +1,52 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "HyFetch"
dynamic = ["version"]
description = "neofetch with flags <3"
readme = "README.md"
authors = [
{ name = "Azalea Gui", email = "me@hydev.org" },
]
requires-python = ">=3.7"
license = "MIT"
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
# Universal dependencies
'typing_extensions; python_version < "3.8"',
# Windows dependencies
'psutil ; platform_system=="Windows"',
'colorama>=0.4.6 ; platform_system=="Windows"',
]
[project.urls]
Homepage = "https://github.com/hykilpikonna/HyFetch"
[project.scripts]
"hyfetch.v1" = "hyfetch.__main__:run_py"
"hyfetch.rs" = "hyfetch.__main__:run_rust"
"hyfetch" = "hyfetch.__main__:run_rust"
[tool.hatch.version]
source = "regex"
path = "Cargo.toml"
regex = 'version = "(?P<version>.+?)"'
[tool.hatch.build]
exclude = ["/tools"]
[tool.hatch.build.targets.wheel.files]
"{py_scripts}/neowofetch" = "hyfetch/scripts/neowofetch"
-58
View File
@@ -1,58 +0,0 @@
#!/usr/bin/env python3
from pathlib import Path
from setuptools import setup, find_namespace_packages
# The directory containing this file
HERE = Path(__file__).parent
# Load version without importing it (see issue #192 if you are confused)
VERSION = [l for l in (HERE / "Cargo.toml").read_text('utf-8').splitlines() if l.startswith("version = ")]
if len(VERSION) != 1:
raise ValueError(f"Cannot determine version from Cargo.toml: {VERSION}")
VERSION = VERSION[0].split('"')[1]
# The text of the README file
README = (HERE / "README.md").read_text('utf-8')
# This call to setup() does all the work
setup(
name="HyFetch",
version=VERSION,
description="neofetch with flags <3",
long_description=README,
long_description_content_type="text/markdown",
url="https://github.com/hykilpikonna/HyFetch",
author="Azalea Gui",
author_email="me@hydev.org",
license="MIT",
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
],
packages=find_namespace_packages(exclude=("tools", "tools.*")),
package_data={'hyfetch': ['hyfetch/*']},
include_package_data=True,
install_requires=[
# Universal dependencies
'typing_extensions; python_version < "3.8"',
# Windows dependencies
'psutil ; platform_system=="Windows"',
'colorama>=0.4.6 ; platform_system=="Windows"',
],
entry_points={
"console_scripts": [
"hyfetch.v1=hyfetch.__main__:run_py",
"hyfetch.rs=hyfetch.__main__:run_rust",
"hyfetch=hyfetch.__main__:run_rust",
]
},
scripts=['hyfetch/scripts/neowofetch']
)
+7 -9
View File
@@ -16,8 +16,9 @@ rm -rf build/
# Remove git from the source code before building
rm -rf hyfetch/git/
# Build python from setup.py
python3 setup.py sdist bdist_wheel
# Build python package
python3 -m pip install build
python3 -m build
# Check
twine check dist/*.tar.gz
@@ -61,11 +62,6 @@ cp "$DIR/../target/x86_64-pc-windows-gnu/release/hyfetch.exe" wheel/hyfetch/rust
sed -i 's/Tag: py3-none-.*/Tag: py3-none-win32/' wheel/*.dist-info/WHEEL
python "$DIR/build_rehash.py" wheel
# Zip to -win32.whl
#new_name=${file/-any/-win32}
#cd wheel && zip -qq -y -r "../$new_name" * && cd ..
#twine check "$new_name"
# Zip to -win_amd64.whl
# Since pypi doesn't allow two identical files with different names to be uploaded
# We need to change the zip content a little bit for win_amd64
@@ -78,8 +74,10 @@ twine check "$new_name"
# =================
# Build for linux
# Now we're done with windows, delete the git folder
rm -rf wheel/git
# Now we're done with windows, delete wheel and unzip again
echo "> Building for other platforms"
rm -rf wheel
unzip -qq "$file" -d wheel
function build_for_platform() {
ff_platform=$1
+3 -9
View File
@@ -1,11 +1,5 @@
### Things to do before deploying...
* [x] Check file permissions (+x)
* [x] Check Shellcheck (should be automatic)
* [x] Update version numbers (`README.md`, `package.json`, `hyfetch/constants.py`, `neofetch`)
* [x] Update distro list in neofetch help (`tools/list_distros.py`)
* [x] Regenerate man page (`help2man ./neofetch > neofetch.1`)
* [ ] Create an RC release and deploy to pypi, try installing and testing on many distros.
* [ ] Change back to stable release, create tag, create GitHub release
* [x] Formally deploy to pypi and npm (`tools/deploy.sh`, `npm publish`)
* [ ] Update ArchLinux AUR and NixOS packaging
* [ ] Update changelog (`README.md`) and commit changes
* [ ] Run `mamba activate 313` (an environment that has build deps e.g. twine)
* [ ] Run `python tools/deploy-release.py {version}`