diff --git a/Cargo.lock b/Cargo.lock index 1d121f63..3b8819f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -283,6 +283,7 @@ dependencies = [ "enterpolation", "fastrand", "fs_extra", + "heck", "indexmap", "itertools", "normpath", diff --git a/crates/hyfetch/Cargo.toml b/crates/hyfetch/Cargo.toml index bfee14da..77dddccf 100644 --- a/crates/hyfetch/Cargo.toml +++ b/crates/hyfetch/Cargo.toml @@ -45,6 +45,10 @@ indexmap = { workspace = true, features = ["std"] } 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 = [] } diff --git a/crates/hyfetch/build.rs b/crates/hyfetch/build.rs index 6560cdec..bfcac692 100644 --- a/crates/hyfetch/build.rs +++ b/crates/hyfetch/build.rs @@ -1,11 +1,15 @@ +use std::collections::HashMap; 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 heck::ToUpperCamelCase; use indexmap::IndexMap; use regex::Regex; +use serde::Deserialize; use unicode_normalization::UnicodeNormalization as _; #[derive(Debug)] @@ -51,12 +55,13 @@ fn main() { else { fs::copy(&src, &dst).expect("Failed to copy file to OUT_DIR"); } } + preset_codegen(&o.join("hyfetch/data/presets.json"), &o.join("presets.rs")) + .expect("couldn't generate preset code"); + export_distros(&o.join("neofetch"), &o); } -fn export_distros

(neofetch_path: P, out_path: &Path) -where - P: AsRef, +fn export_distros(neofetch_path: &Path, out_path: &Path) { let distros = parse_ascii_distros(neofetch_path); let mut variants = IndexMap::with_capacity(distros.len()); @@ -201,12 +206,8 @@ impl Distro { } /// Parses ascii distros from neofetch script. -fn parse_ascii_distros

(neofetch_path: P) -> Vec -where - P: AsRef, +fn parse_ascii_distros(neofetch_path: &Path) -> Vec { - let neofetch_path = neofetch_path.as_ref(); - let nf = { let nf = fs::read_to_string(neofetch_path).expect("couldn't read neofetch script"); @@ -272,3 +273,70 @@ where .filter_map(|block| parse_block(block)) .collect() } + +// Preset parsing +#[derive(Deserialize, Debug)] +#[serde(untagged)] +enum PresetEntry { + Simple(Vec), + Complex { colors: Vec, weights: Option> }, +} + +type PresetMap = HashMap; + +fn preset_codegen(json_path: &Path, out_path: &Path) -> Result<(), Box> { + // 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).unwrap()); + + // 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::>().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::>().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(()) +} diff --git a/crates/hyfetch/src/bin/hyfetch.rs b/crates/hyfetch/src/bin/hyfetch.rs index 8145048b..517d7ee2 100644 --- a/crates/hyfetch/src/bin/hyfetch.rs +++ b/crates/hyfetch/src/bin/hyfetch.rs @@ -25,7 +25,8 @@ use hyfetch::models::Config; #[cfg(feature = "macchina")] use hyfetch::neofetch_util::macchina_path; use hyfetch::neofetch_util::{self, add_pkg_path, fastfetch_path, get_distro_ascii, get_distro_name, literal_input, ColorAlignment, NEOFETCH_COLORS_AC, NEOFETCH_COLOR_PATTERNS, TEST_ASCII}; -use hyfetch::presets::{AssignLightness, ColorProfile, Preset}; +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}; diff --git a/crates/hyfetch/src/color_profile.rs b/crates/hyfetch/src/color_profile.rs new file mode 100644 index 00000000..9f477c87 --- /dev/null +++ b/crates/hyfetch/src/color_profile.rs @@ -0,0 +1,232 @@ +use std::iter; +use std::num::{NonZeroU8, NonZeroUsize}; + +use anyhow::{anyhow, 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>, +} + +#[derive(Clone, PartialEq, Debug)] +pub enum AssignLightness { + Replace(Lightness), + ClampMax(Lightness), + ClampMin(Lightness), +} + +impl ColorProfile { + pub fn new(colors: Vec>) -> Self { + Self { colors } + } + + pub fn from_hex_colors(hex_colors: Vec) -> anyhow::Result + where + S: AsRef, + { + let colors = hex_colors + .into_iter() + .map(|s| s.as_ref().parse()) + .collect::>() + .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) -> anyhow::Result { + 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) -> anyhow::Result { + 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( + &self, + txt: S, + color_mode: AnsiMode, + foreground_background: ForegroundBackground, + space_only: bool, + ) -> anyhow::Result + where + S: AsRef, + { + 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 = + 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::::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 = + 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> = rgb_f32_colors + .into_iter() + .map(Srgb::::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> = unique_colors.into_iter().map(|c| c.into()).collect(); + Self::new(unique_colors) + } +} diff --git a/crates/hyfetch/src/lib.rs b/crates/hyfetch/src/lib.rs index f4d54d74..1ea28bb8 100644 --- a/crates/hyfetch/src/lib.rs +++ b/crates/hyfetch/src/lib.rs @@ -9,3 +9,4 @@ pub mod presets; pub mod pride_month; pub mod types; pub mod utils; +pub mod color_profile; diff --git a/crates/hyfetch/src/presets.rs b/crates/hyfetch/src/presets.rs index 6e20ad7d..9b2ac040 100644 --- a/crates/hyfetch/src/presets.rs +++ b/crates/hyfetch/src/presets.rs @@ -1,956 +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, - - Libragender, Librafeminine, Libramasculine, Libraandrogyne, Libranonbinary, - - Fluidfluxa, Fluidfluxb, - - Transbian, - - Autism, - - Cenelian, - - Transneutral, -} - -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct ColorProfile { - pub colors: Vec>, -} - -#[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" - ]), - - Self::Libragender => ColorProfile::from_hex_colors(vec![ - "#000000", "#808080", "#92D8E9", "#FFF544", "#FFB0CA", "#808080", "#000000" - ]), - - Self::Librafeminine => ColorProfile::from_hex_colors(vec![ - "#000000", "#A3A3A3", "#FFFFFF", "#C6568F", "#FFFFFF", "#A3A3A3", "#000000" - ]), - - Self::Libramasculine => ColorProfile::from_hex_colors(vec![ - "#000000", "#A3A3A3", "#FFFFFF", "#56C5C5", "#FFFFFF", "#A3A3A3", "#000000" - ]), - - Self::Libraandrogyne => ColorProfile::from_hex_colors(vec![ - "#000000", "#A3A3A3", "#FFFFFF", "#9186B1", "#FFFFFF", "#A3A3A3", "#000000" - ]), - - Self::Libranonbinary => ColorProfile::from_hex_colors(vec![ - "#000000", "#A3A3A3", "#FFFFFF", "#FFF987", "#FFFFFF", "#A3A3A3", "#000000" - ]), - - Self::Fluidfluxa => ColorProfile::from_hex_colors(vec![ - "#ff115f", "#a34aa3", "#00a4e7", "#ffdf00", "#000000", "#ffed71", "#85daff", "#dbadda", "#fe8db1" - ]), - - Self::Fluidfluxb => ColorProfile::from_hex_colors(vec![ - "#c6d1d2", "#f47b9d", "#f09f9b", "#e3f09e", "#75eeea", "#52d2ed", "#c6d1d2" - ]), - - Self::Transbian => ColorProfile::from_hex_colors(vec![ - "#03A3E6", "#F8B4CD","#FAFBF9", "#FA9C57", "#A80864" - ]), - - Self::Autism => ColorProfile::from_hex_colors(vec![ - "#c94a49", "#de7554", "#dbb667", "#6fa35d", "#2e7574", "#232828" - ]), - - Self::Cenelian => ColorProfile::from_hex_colors(vec![ - "#ffe7b6", "#93554a", "#52203a", "#7e4a93", "#99afd6" - ]), - - Self::Transneutral => ColorProfile::from_hex_colors(vec![ - "#74dfff", "#fffdb3", "#fffc75", "#fff200", "#fffc75", "#fffdb3", "#fe8cbf" - ]), - }) - .expect("preset color profiles should be valid") - } -} - -impl ColorProfile { - pub fn new(colors: Vec>) -> Self { - Self { colors } - } - - pub fn from_hex_colors(hex_colors: Vec) -> Result - where - S: AsRef, - { - let colors = hex_colors - .into_iter() - .map(|s| s.as_ref().parse()) - .collect::>() - .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) -> Result { - 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 { - 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( - &self, - txt: S, - color_mode: AnsiMode, - foreground_background: ForegroundBackground, - space_only: bool, - ) -> Result - where - S: AsRef, - { - 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 = - 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::::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 = - 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> = rgb_f32_colors - .into_iter() - .map(Srgb::::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> = unique_colors.into_iter().map(|c| c.into()).collect(); - Self::new(unique_colors) - } -} +include!(concat!(env!("OUT_DIR"), "/presets.rs")); diff --git a/hyfetch/data/presets.json b/hyfetch/data/presets.json index 88a939a3..e9447fea 100644 --- a/hyfetch/data/presets.json +++ b/hyfetch/data/presets.json @@ -214,11 +214,11 @@ "colors": ["#000000", "#A3A3A3", "#FFFFFF", "#FFF987", "#FFFFFF", "#A3A3A3", "#000000"], "comment": "Sourced from https://lgbtqia.wiki/wiki/Libranonbinary" }, - "fluidflux A": { + "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" }, - "fluidflux B": ["#C6D1D2", "#F47B9D", "#F09F9B", "#E3F09E", "#75EEEA", "#52D2ED", "#C6D1D2"], + "fluidflux2": ["#C6D1D2", "#F47B9D", "#F09F9B", "#E3F09E", "#75EEEA", "#52D2ED", "#C6D1D2"], "beiyang": ["#DF1B12", "#FFC600", "#01639D", "#FFFFFF", "#000000"], "burger": ["#F3A26A", "#498701", "#FD1C13", "#7D3829", "#F3A26A"],