Compare commits
35 Commits
2.0.2
...
neofetch-8.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| dee984f931 | |||
| 959d42ee9c | |||
| eb349aa1aa | |||
| d5751e06af | |||
| 981602b690 | |||
| a8d752aa9f | |||
| c57e8a4a49 | |||
| 67ae918ae8 | |||
| e788f93c2b | |||
| 75a2e9d8d3 | |||
| 23888c0e3e | |||
| e5ad9e564d | |||
| 328381b336 | |||
| 28a181d97b | |||
| bb514f8fd9 | |||
| b585ee1e26 | |||
| de2141347e | |||
| ba88581ed4 | |||
| 932042b62c | |||
| f5c5e31691 | |||
| 15ca855a04 | |||
| 666d2dc90a | |||
| 34583294c6 | |||
| 03615ab4ee | |||
| c722c73e79 | |||
| 8f5199974b | |||
| 8168877fb1 | |||
| 4e20b18d45 | |||
| 5dc1709f58 | |||
| fb1e35172e | |||
| fc9292be3f | |||
| 3f41cb40e2 | |||
| 729024a45f | |||
| ef1407d00e | |||
| 075fc467d2 |
Generated
+325
-243
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -3,7 +3,7 @@ resolver = "2"
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.0.2"
|
||||
version = "2.0.3"
|
||||
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 }
|
||||
|
||||
@@ -129,6 +129,43 @@ cargo install --git https://github.com/hykilpikonna/hyfetch
|
||||
|
||||
<!-- CHANGELOG STARTS HERE --->
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -9,3 +9,4 @@ pub mod presets;
|
||||
pub mod pride_month;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
pub mod color_profile;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -113,16 +113,12 @@ where
|
||||
};
|
||||
|
||||
if let Some(selected) = find_selection(&selection, options) {
|
||||
writeln!(io::stdout()).context("failed to write to stdout")?;
|
||||
println!();
|
||||
|
||||
return Ok(selected);
|
||||
} else {
|
||||
let options_text = options.iter().map(AsRef::as_ref).join("|");
|
||||
writeln!(
|
||||
io::stdout(),
|
||||
"Invalid selection! {selection} is not one of {options_text}"
|
||||
)
|
||||
.context("failed to write message to stdout")?;
|
||||
println!("Invalid selection! {selection} is not one of {options_text}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +281,7 @@ pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec<String>>)
|
||||
}
|
||||
|
||||
/// Gets distro ascii width and height, ignoring color code.
|
||||
pub fn ascii_size<S>(asc: S) -> Result<(u8, u8)>
|
||||
pub fn ascii_size<S>(asc: S) -> Result<(u16, u16)>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
@@ -307,25 +303,11 @@ where
|
||||
return Ok((0, 0));
|
||||
}
|
||||
|
||||
let width = asc
|
||||
.lines()
|
||||
.map(|line| line.graphemes(true).count())
|
||||
.max()
|
||||
let width = asc.lines()
|
||||
.map(|line| line.graphemes(true).count()).max()
|
||||
.expect("line iterator should not be empty");
|
||||
let width: u8 = width.try_into().with_context(|| {
|
||||
format!(
|
||||
"`asc` should not have more than {limit} characters per line",
|
||||
limit = u8::MAX
|
||||
)
|
||||
})?;
|
||||
let height = asc.lines().count();
|
||||
let height: u8 = height.try_into().with_context(|| {
|
||||
format!(
|
||||
"`asc` should not have more than {limit} lines",
|
||||
limit = u8::MAX
|
||||
)
|
||||
})?;
|
||||
|
||||
let width: u16 = width.try_into().context("ascii art width should fit in u16")?;
|
||||
let height: u16 = asc.lines().count().try_into().context("ascii art height should fit in u16")?;
|
||||
Ok((width, height))
|
||||
}
|
||||
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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
@@ -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.3" "User Commands"
|
||||
.SH NAME
|
||||
Version: \- manual page for Version: 2.0.2
|
||||
Version: \- manual page for Version: 2.0.3
|
||||
.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
@@ -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.3" "User Commands"
|
||||
.SH NAME
|
||||
Neofetch \- manual page for Neofetch 8.0.2
|
||||
Neofetch \- manual page for Neofetch 8.0.3
|
||||
.SH SYNOPSIS
|
||||
.B neofetch
|
||||
\fI\,func_name --option "value" --option "value"\/\fR
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from .py import run_py
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
VERSION = '2.0.2'
|
||||
from __future__ import annotations
|
||||
|
||||
VERSION = '2.0.3'
|
||||
|
||||
+11
-3
@@ -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:
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
@@ -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
@@ -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}')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from . import main
|
||||
from .color_util import printc
|
||||
|
||||
|
||||
def run_py():
|
||||
try:
|
||||
main.run()
|
||||
|
||||
+4
-3
@@ -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'
|
||||
|
||||
@@ -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.3
|
||||
|
||||
# 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
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "neowofetch",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"description": "Updated neofetch",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user