Compare commits

...

71 Commits

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

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

* [-] Remove test ascii

---------

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

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

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

* [F] Fix nixos_small logo missing line break

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

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

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

related pull: https://github.com/hykilpikonna/hyfetch/issues/418
2025-08-23 17:34:57 -07:00
azalea d4560e3edb [U] Release 2.0.1 2025-08-21 12:56:30 -04:00
azalea 62b4c27404 [+] Changelog 2025-08-21 12:55:45 -04:00
azalea 169368a784 [F] :) 2025-08-21 12:46:00 -04:00
azalea 3e47640c1a [F] Maybe fix git install 2025-08-21 12:42:12 -04:00
azalea 6ddd762b35 [F] ? 2025-08-21 12:37:37 -04:00
azalea 713bcce0ec [F] Fix git install 2025-08-21 12:35:04 -04:00
azalea fdfd9816f5 [F] Fix deploy script 2025-08-21 12:06:24 -04:00
azalea d6f9d1f569 [F] Fix june flicker #408 2025-08-21 11:48:24 -04:00
azalea b28aa0ddf2 [U] Bump 2025-08-21 11:23:37 -04:00
azalea a5e784c528 [F] Fix distro name detection 2025-08-21 11:21:48 -04:00
azalea 93b4dd3471 [U] Bump 2025-08-21 11:02:20 -04:00
azalea d15afe579f [O] Optimize calculations 2025-08-21 11:01:59 -04:00
azalea f79aca3c65 [-] Remove unused warning 2025-08-21 10:09:00 -04:00
azalea fa3fc59a5f [F] Fix trying to get backend from non-existing backend 2025-08-21 10:08:06 -04:00
azalea afa3c1dfb9 [F] Fix cache path might be missing 2025-08-21 10:05:02 -04:00
azalea cf6b77a1b7 [F] 💢💢💢💢💢💢💢💢💢💢💢 2025-08-21 09:52:03 -04:00
azalea b58425badd [F] 💢💢💢💢💢 2025-08-21 09:47:48 -04:00
azalea 8c1713c37d [F] Forgot to bump lock 2025-08-21 09:29:42 -04:00
azalea 3c7c449da4 [F] Maybe this can work 2025-08-21 09:28:44 -04:00
azalea 2fe4b3f287 [F] Fix cargo publish #405 2025-08-21 09:14:05 -04:00
azalea 6a80928f10 [F] Fix path 2025-08-21 08:35:42 -04:00
azalea 4ac698817d [F] Fix cargo publish #405 2025-08-21 08:31:41 -04:00
azalea 13acc0b6b6 [U] Bump 2025-08-21 08:31:41 -04:00
Ven0m0 455f0382e7 [+] Use cloudflare for public IP field (#416)
* Update neofetch

* [F] Fix drill cloudflare and add fallback

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-08-21 05:05:08 -07:00
azalea 0c9e4db742 [F] Fix #418 2025-08-21 03:17:56 -04:00
Aiden Bohlander aee058e591 Update logic to not mark the pride month animation as shown if --june is explicitly specified. (#411)
Update logic to not mark the pride month animation as shown if --june is explicitly specified.
2025-06-02 04:03:08 -04:00
azalea b74787768f [+] Bazzite (#406) 2025-05-27 11:43:32 +09:00
Zulta 218c535d06 Add Rhino Linux Logo (#407)
* added Rhino Linux Logo

* updated rhino.py

* fixed the ;; on line 8745

* [F] Fix python generation

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-05-25 21:59:28 -04:00
stella :3c 76ae5e507a [FEATURE PROTOTYPE] adds step on interactive setup which prompts the user for either the default or small variant of their distro logo. (#392)
* adds step on interactive setup which prompts the user for either the default or small variant of their distro logo.

* fixed distro small logo variant check

* fixes logo vec index overflow error

* now takes --distro as an input when attempting to fetch a small logo variant

* fixes an incorrect error message
2025-04-06 01:05:04 -04:00
Yuki Kurosawa d2dfd950a3 Update KSLinux detection due to kpt and kpm command is removed (#395)
* Update KSLinux detection due to kpt and kpm command is removed

* Fix Bugs
2025-04-06 01:03:39 -04:00
Paul 45ee7e199e Fix unwanted space in Python version (#401)
Before:

```
Python: Python 3.13.2 [Clang 16.0.0 ]
```

After:

```
Python: Python 3.13.2 [Clang 16.0.0]
```
2025-04-06 01:02:46 -04:00
azalea 2cfea54a88 [+] Cisgender flag for #386
Okay I think if I added this, someone will ask about the straight flag again.

In terms of historical context, the cisgender flag has been used neutrally, and it has not been used as a symbol of dismissal like the straight flag did. While some people question whether it's necessary to have a pride flag for the majority identity that do not face discrimination, its existence isn't perceived as offensive by the LGBTQ+ community. So I judged it would be harmless to add the cisgender flag, while adding the straight flag would be harmful.
2025-02-16 17:41:21 -05:00
Alice 0cc6ddbca8 [PR] Auto detect light/dark mode, changing the theme accordingly (#380)
* disable clippy lint

* fmt

* move detect bg into a function

* auto detect light/dark opt

* auto detect dark cli opt

* optional light-dark

* cli opt

* nicer looking output

* [-] Revert code style changes

* [-] Revert code style changes

* [-] Revert code style changes

* [-] Revert

* make the default theme dark

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-02-16 16:58:45 -05:00
Streaksu 8a536c30f0 [PR] Separate Gloire from Ironclad (#388) 2025-02-15 09:08:03 -05:00
Un1q32 5c24bd443a [PR] fix cpu fetching code for leopard and tiger (#387)
* fix cpu fetching code for leopard and tiger

* some 10.5 systems have this sysctl, just check if it exists like this
2025-02-15 09:03:48 -05:00
Un1q32 e691b2d9da [PR] fix memory code for old macOS versions (#389)
* fix memory code for old macOS versions

* don't print memory at all here, instead of just printing zeros
2025-02-15 10:48:21 +08:00
Ameyama Izumi 5ec7b4f01a [+] Add Kessoku Band flag for meme (#384)
* [+] Add Kessoku Band flag for meme

* [F] Fix key

---------

Co-authored-by: Azalea <22280294+hykilpikonna@users.noreply.github.com>
2025-01-19 05:55:17 +08:00
azalea f38b754783 [F] Port fix #383 to rust 2025-01-18 16:51:17 -05:00
jjanzen 211beb7b6a Limit permutations to 1000 (#383)
limit permutations to 1000

Co-authored-by: jjanzen <jjanzen@jjanzen.ca>
2025-01-19 03:08:12 +08:00
azalea 02c64ef281 [F] Fix #379 2025-01-06 04:53:25 -05:00
azalea 4e9c510240 [F] Fix #378 2025-01-06 04:47:46 -05:00
zenobit d6b559fb40 [F] am count packages fix (#375) 2025-01-04 03:16:00 +08:00
38 changed files with 1798 additions and 1164 deletions
+12 -1
View File
@@ -120,4 +120,15 @@ webhook-log
start_moderation.sh start_moderation.sh
gh_moderator.toml gh_moderator.toml
moderator-data moderator-data
hyfetch/git hyfetch/git
build
dist
target
.vscode
.idea
*.iml
*.egg-info
__pycache__
*.pyc
*.pyo
*.pyd
Generated
+332 -244
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "2.0.0-rc1" version = "2.0.2"
authors = ["Azalea Gui <azalea@hydev.org>"] authors = ["Azalea Gui <azalea@hydev.org>"]
edition = "2021" edition = "2021"
rust-version = "1.75.0" 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 } anstream = { version = "0.6.14", default-features = false }
anyhow = { version = "1.0.86", default-features = false } anyhow = { version = "1.0.86", default-features = false }
bpaf = { version = "0.9.12", 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 } 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 } enable-ansi-support = { version = "0.2.1", default-features = false }
enterpolation = { version = "0.2.1", default-features = false } enterpolation = { version = "0.2.1", default-features = false }
fastrand = { version = "2.1.0", default-features = false } fastrand = { version = "2.1.0", default-features = false }
indexmap = { version = "2.2.6", 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 } normpath = { version = "1.2.0", default-features = false }
palette = { version = "0.7.6", default-features = false } palette = { version = "0.7.6", default-features = false }
regex = { version = "1.10.5", 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_json = { version = "1.0.118", default-features = false }
serde_path_to_error = { version = "0.1.16", default-features = false } serde_path_to_error = { version = "0.1.16", default-features = false }
shell-words = { version = "1.1.0", 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 } supports-color = { version = "3.0.0", default-features = false }
tempfile = { version = "3.10.1", default-features = false } tempfile = { version = "3.10.1", default-features = false }
terminal-colorsaurus = { version = "0.4.3", default-features = false } terminal-colorsaurus = { version = "1.0.0", default-features = false }
terminal_size = { version = "0.3.0", default-features = false } terminal_size = { version = "0.4.3", default-features = false }
thiserror = { version = "1.0.61", default-features = false } thiserror = { version = "1.0.61", default-features = false }
time = { version = "0.3.36", 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 = { version = "0.1.40", default-features = false }
tracing-subscriber = { version = "0.3.18", default-features = false } tracing-subscriber = { version = "0.3.18", default-features = false }
unicode-normalization = { version = "0.1.23", default-features = false } unicode-normalization = { version = "0.1.23", default-features = false }
+55 -1
View File
@@ -124,11 +124,65 @@ Updates to `neowofetch` begins with the emoji 🖼️
Note: You can install the latest nightly version by using: Note: You can install the latest nightly version by using:
```sh ```sh
pip install git+https://github.com/hykilpikonna/hyfetch.git@master cargo install --git https://github.com/hykilpikonna/hyfetch
``` ```
<!-- CHANGELOG STARTS HERE ---> <!-- CHANGELOG STARTS HERE --->
### 2.0.2
This is a small patch release that adds more flags and fixes some bugs from the recent Rust rewrite.
* 🏳️‍🌈 **New Flags**: Added new pride flags for Nullflux ([#397](https://github.com/hykilpikonna/hyfetch/pull/397)), Hypergender ([#422](https://github.com/hykilpikonna/hyfetch/pull/422)), Fictosexual ([#394](https://github.com/hykilpikonna/hyfetch/pull/394)), and Adipophilia ([#424](https://github.com/hykilpikonna/hyfetch/pull/424)).
* 🐛 **Bug Fixes**:
* Fixed a critical bug where custom `fastfetch` configurations could break hyfetch ([#420](https://github.com/hykilpikonna/hyfetch/pull/420)).
* Corrected the `nixos_small` ASCII logo to display properly ([#421](https://github.com/hykilpikonna/hyfetch/pull/421)).
* 🔧 **Improvements**:
* Improved a warning message for better clarity ([#419](https://github.com/hykilpikonna/hyfetch/issues/419)).
### 2.0.1
(changelog is generated by Gemini from commit history)
**🦀 The Rust Rewrite!**
This is a massive update, rewriting the entire hyfetch core from Python to Rust for significantly improved performance, reliability, and maintainability. A huge thank you to **@teohhanhui** and all the contributors who made this possible!
* 🚀 **Complete Rewrite in Rust**: The entire codebase has been ported to Rust, resulting in major performance improvements and a smaller binary size.
* ⚙️ **New Backends**: In addition to neofetch and fastfetch, hyfetch now supports **macchina** as a backend option.
* 🌈 **Improved June Animation**: The `--june` pride month animation is now smoother, flicker-free ([#408](https://github.com/hykilpikonna/hyfetch/pull/408)).
* 🪟 **Native Windows Support**: The Rust version provides much-improved support for Windows.
**✨ Features & Enhancements**
* 🎨 **Automatic Theme Detection**: Hyfetch can now automatically detect your terminal's light/dark mode (when auto_detect_light_dark is true) ([#380](https://github.com/hykilpikonna/hyfetch/pull/380)).
* ✍️ **Font Logos**: Added a new feature to display a logo created from your system's font.
* 🌐 **Cloudflare for Public IP**: Now uses Cloudflare to fetch the public IP address, with a fallback option ([#416](https://github.com/hykilpikonna/hyfetch/pull/416)).
* 🖼️ **Interactive Logo Selection**: An interactive setup step now allows you to choose between default and small logo variants for your distro ([#392](https://github.com/hykilpikonna/hyfetch/pull/392)).
* 🏳️‍🌈 **New Pride Flags**: Added flags for Cisgender ([#386](https://github.com/hykilpikonna/hyfetch/pull/386)), Kessoku Band ([#384](https://github.com/hykilpikonna/hyfetch/pull/384)), and Watermelon Cream Lozenges ([#369](https://github.com/hykilpikonna/hyfetch/pull/369)) for memes.
* 📄 **Page Navigation**: Added `n` and `p` as shorthands for next/previous page selection and implemented page number roll-over ([#372](https://github.com/hykilpikonna/hyfetch/pull/372)).
**🖼️ New & Updated Distro Support**
* **Added Bazzite** ([#406](https://github.com/hykilpikonna/hyfetch/pull/406))
* **Added Rhino Linux Logo** ([#407](https://github.com/hykilpikonna/hyfetch/pull/407))
* **Updated openSUSE Logos & Added Slowroll**
* **Updated KSLinux Detection** ([#395](https://github.com/hykilpikonna/hyfetch/pull/395))
**🐛 Bug Fixes**
* **macOS**: Fixed CPU and memory fetching on older macOS versions like Leopard and Tiger ([#387](https://github.com/hykilpikonna/hyfetch/pull/387), [#389](https://github.com/hykilpikonna/hyfetch/pull/389)).
* **Packaging**: Resolved several issues with `cargo publish` to ensure reliable package deployment ([#405](https://github.com/hykilpikonna/hyfetch/pull/405)).
* **Performance**: Sped up RPM package counting by skipping digest and signature verification ([#371](https://github.com/hykilpikonna/hyfetch/pull/371)).
* **Animation**: Updated logic to ensure the pride month animation is always shown when `--june` is explicitly used ([#411](https://github.com/hykilpikonna/hyfetch/pull/411)).
* **General**: Fixed numerous bugs related to builds, path detection, cache handling, and distro name detection.
* **Formatting**: Fixed an unwanted space in the Python version output ([#401](https://github.com/hykilpikonna/hyfetch/pull/401)).
**🔧 Maintenance**
* **Documentation**: Updated the README with additional credits for Slackware contributors ([#365](https://github.com/hykilpikonna/hyfetch/pull/365)).
* **Build System**: Overhauled the build scripts for Rust, including support for `musl` static builds and cross-compilation for macOS and Windows.
### 1.99.0 ### 1.99.0
This version would be the last version of HyFetch on Python as we migrate to Rust (Huge thanks to everyone on [#317](https://github.com/hykilpikonna/hyfetch/pull/317)!). It will also be an effort to start a transition that phases out the neowofetch/neofetch backend in favor of FastFetch, since the time needed to maintain the NF backend currently exceed our capacity. If you are willing to help maintaining it, please let us know! This version would be the last version of HyFetch on Python as we migrate to Rust (Huge thanks to everyone on [#317](https://github.com/hykilpikonna/hyfetch/pull/317)!). It will also be an effort to start a transition that phases out the neowofetch/neofetch backend in favor of FastFetch, since the time needed to maintain the NF backend currently exceed our capacity. If you are willing to help maintaining it, please let us know!
+1
View File
@@ -44,6 +44,7 @@ which = { workspace = true, features = [] }
indexmap = { workspace = true, features = ["std"] } indexmap = { workspace = true, features = ["std"] }
regex = { workspace = true, features = ["perf", "std", "unicode"] } regex = { workspace = true, features = ["perf", "std", "unicode"] }
unicode-normalization = { workspace = true, features = ["std"] } unicode-normalization = { workspace = true, features = ["std"] }
fs_extra = "1.3.0"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
enable-ansi-support = { workspace = true, features = [] } enable-ansi-support = { workspace = true, features = [] }
+27 -10
View File
@@ -1,7 +1,9 @@
use std::env;
use std::fmt::Write as _; use std::fmt::Write as _;
use std::path::Path; use std::fs;
use std::{env, fs}; use std::path::{Path, PathBuf};
use fs_extra::dir::{CopyOptions};
use indexmap::IndexMap; use indexmap::IndexMap;
use regex::Regex; use regex::Regex;
use unicode_normalization::UnicodeNormalization as _; use unicode_normalization::UnicodeNormalization as _;
@@ -23,18 +25,33 @@ impl AsciiDistro {
} }
} }
fn anything_that_exist(paths: &[&Path]) -> Option<PathBuf> {
paths.iter().copied().find(|p| p.exists()).map(Path::to_path_buf)
}
fn main() { fn main() {
let neofetch_path = Path::new(env!("CARGO_WORKSPACE_DIR")).join("neofetch"); // 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());
println!( for file in &["neofetch", "hyfetch/data"] {
"cargo:rerun-if-changed={neofetch_path}", let src = anything_that_exist(&[
neofetch_path = neofetch_path.display() &dir.join(file),
); &dir.join("../../").join(file),
]).expect("couldn't find neofetch");
let dst = o.join(file);
println!("cargo:rerun-if-changed={}", src.display());
let out_dir = env::var_os("OUT_DIR").unwrap(); // Copy either file or directory
let out_path = Path::new(&out_dir); if src.is_dir() {
let opt = CopyOptions { overwrite: true, copy_inside: true, ..CopyOptions::default() };
println!("copying {} to {}", src.display(), dst.display());
fs_extra::dir::copy(&src, &dst, &opt).expect("Failed to copy directory to OUT_DIR");
}
else { fs::copy(&src, &dst).expect("Failed to copy file to OUT_DIR"); }
}
export_distros(neofetch_path, out_path); export_distros(&o.join("neofetch"), &o);
} }
fn export_distros<P>(neofetch_path: P, out_path: &Path) fn export_distros<P>(neofetch_path: P, out_path: &Path)
+2 -2
View File
@@ -61,8 +61,8 @@ impl RawAsciiArt {
Ok(NormalizedAsciiArt { Ok(NormalizedAsciiArt {
lines, lines,
w, w: w.try_into().context("width does not fit in u8")?,
h, h: h.try_into().context("height does not fit in u8")?,
fg: self.fg.clone(), fg: self.fg.clone(),
}) })
} }
+314 -261
View File
@@ -2,7 +2,8 @@ use std::borrow::Cow;
use std::cmp; use std::cmp;
use std::fmt::Write as _; use std::fmt::Write as _;
use std::fs::{self, File}; 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::iter::zip;
use std::num::NonZeroU8; use std::num::NonZeroU8;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -13,17 +14,19 @@ use deranged::RangedU8;
use enterpolation::bspline::BSpline; use enterpolation::bspline::BSpline;
use enterpolation::{Curve as _, Generator as _}; use enterpolation::{Curve as _, Generator as _};
use hyfetch::ascii::RawAsciiArt; use hyfetch::ascii::RawAsciiArt;
use hyfetch::ascii::NormalizedAsciiArt;
use hyfetch::cli_options::options; use hyfetch::cli_options::options;
use hyfetch::color_util::{ use hyfetch::color_util::{
clear_screen, color, printc, ContrastGrayscale as _, ForegroundBackground, Lightness, clear_screen, color, printc, ContrastGrayscale as _, ForegroundBackground, Lightness,
NeofetchAsciiIndexedColor, PresetIndexedColor, Theme as _, ToAnsiString as _, NeofetchAsciiIndexedColor, PresetIndexedColor, Theme as _, ToAnsiString as _,
}; };
use hyfetch::distros::Distro;
use hyfetch::models::Config; use hyfetch::models::Config;
#[cfg(feature = "macchina")] #[cfg(feature = "macchina")]
use hyfetch::neofetch_util::macchina_path; use hyfetch::neofetch_util::macchina_path;
use hyfetch::neofetch_util::{self, add_pkg_path, fastfetch_path, get_distro_ascii, literal_input, ColorAlignment, NEOFETCH_COLORS_AC, NEOFETCH_COLOR_PATTERNS, TEST_ASCII}; 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::presets::{AssignLightness, ColorProfile, Preset};
use hyfetch::pride_month; use hyfetch::{pride_month, printc};
use hyfetch::types::{AnsiMode, Backend, TerminalTheme}; use hyfetch::types::{AnsiMode, Backend, TerminalTheme};
use hyfetch::utils::{get_cache_path, input}; use hyfetch::utils::{get_cache_path, input};
use hyfetch::font_logo::get_font_logo; use hyfetch::font_logo::get_font_logo;
@@ -62,15 +65,12 @@ fn main() -> Result<()> {
}); });
if options.test_print { if options.test_print {
let asc = get_distro_ascii(distro, backend).context("failed to get distro ascii")?; println!("{asc}", asc = get_distro_ascii(distro, backend)?.asc);
writeln!(io::stdout(), "{asc}", asc = asc.asc)
.context("failed to write ascii to stdout")?;
return Ok(()); return Ok(());
} }
if options.print_font_logo { if options.print_font_logo {
let logo = get_font_logo(backend).context("failed to get font logo")?; println!("{}", get_font_logo(backend)?);
writeln!(io::stdout(), "{}", logo).context("failed to write logo to stdout")?;
return Ok(()); return Ok(());
} }
@@ -87,7 +87,16 @@ fn main() -> Result<()> {
}; };
let color_mode = options.mode.unwrap_or(config.mode); let color_mode = options.mode.unwrap_or(config.mode);
let theme = config.light_dark; let auto_detect_light_dark = options
.auto_detect_light_dark
.unwrap_or_else(|| config.auto_detect_light_dark.unwrap_or(false));
let theme = if auto_detect_light_dark {
let res = det_bg();
res?.map(|bg| bg.theme())
.unwrap_or(config.light_dark.unwrap_or_default())
} else {
config.light_dark.unwrap_or_default()
};
// Check if it's June (pride month) // Check if it's June (pride month)
let now = let now =
@@ -99,16 +108,9 @@ fn main() -> Result<()> {
if show_pride_month && !config.pride_month_disable { if show_pride_month && !config.pride_month_disable {
pride_month::start_animation(color_mode).context("failed to draw pride month animation")?; pride_month::start_animation(color_mode).context("failed to draw pride month animation")?;
writeln!( println!("\nHappy pride month!\n(You can always view the animation again with `hyfetch --june`)\n");
io::stdout(),
"\nHappy pride month!\n(You can always view the animation again with `hyfetch \
--june`)\n"
)
.context("failed to write message to stdout")?;
if !june_path.is_file() { if !june_path.is_file() && !options.june {
fs::create_dir_all(&cache_path)
.with_context(|| format!("failed to create cache dir {cache_path:?}"))?;
File::create(&june_path) File::create(&june_path)
.with_context(|| format!("failed to create file {june_path:?}"))?; .with_context(|| format!("failed to create file {june_path:?}"))?;
} }
@@ -120,9 +122,43 @@ fn main() -> Result<()> {
let backend = options.backend.unwrap_or(config.backend); let backend = options.backend.unwrap_or(config.backend);
let args = options.args.as_ref().or(config.args.as_ref()); 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 // Get preset
let preset = options.preset.unwrap_or(config.preset); let preset_string = options.preset.as_deref().unwrap_or(&config.preset);
let color_profile = preset.color_profile(); let color_profile = parse_preset_string(preset_string)?;
debug!(?color_profile, "color profile"); debug!(?color_profile, "color profile");
// Lighten // Lighten
@@ -131,11 +167,23 @@ fn main() -> Result<()> {
} else if let Some(lightness) = options.lightness { } else if let Some(lightness) = options.lightness {
color_profile.with_lightness(AssignLightness::Replace(lightness)) color_profile.with_lightness(AssignLightness::Replace(lightness))
} else { } else {
color_profile.with_lightness_adaptive(config.lightness(), theme) color_profile.with_lightness_adaptive(
config
.lightness
.unwrap_or_else(|| Config::default_lightness(theme)),
theme,
)
}; };
debug!(?color_profile, "lightened color profile"); 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 { RawAsciiArt {
asc: fs::read_to_string(&path) asc: fs::read_to_string(&path)
.with_context(|| format!("failed to read ascii from {path:?}"))?, .with_context(|| format!("failed to read ascii from {path:?}"))?,
@@ -187,6 +235,22 @@ fn load_config(path: &PathBuf) -> Result<Option<Config>> {
Ok(Some(config)) Ok(Some(config))
} }
fn det_bg() -> Result<Option<Srgb<u8>>, terminal_colorsaurus::Error> {
if !io::stdout().is_terminal() {
return Ok(None);
}
background_color(QueryOptions::default())
.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(_)) {
Ok(None)
} else {
Err(err)
}
})
}
/// Creates config interactively. /// Creates config interactively.
/// ///
/// The config is automatically stored to file. /// The config is automatically stored to file.
@@ -197,30 +261,15 @@ fn create_config(
backend: Backend, backend: Backend,
debug_mode: bool, debug_mode: bool,
) -> Result<Config> { ) -> Result<Config> {
// Detect terminal environment (doesn't work for all terminal emulators, let det_bg = det_bg()?;
// especially on Windows)
let det_bg = if io::stdout().is_terminal() {
match background_color(QueryOptions::default()) {
Ok(bg) => Some(Srgb::<u16>::new(bg.r, bg.g, bg.b).into_format::<u8>()),
Err(terminal_colorsaurus::Error::UnsupportedTerminal) => None,
Err(err) => {
return Err(err).context("failed to get terminal background color");
},
}
} else {
None
};
debug!(?det_bg, "detected background color"); debug!(?det_bg, "detected background color");
let det_ansi = supports_color::on(supports_color::Stream::Stdout).map(|color_level| { let det_ansi = supports_color::on(supports_color::Stream::Stdout).map(|color_level| {
#[allow(clippy::if_same_then_else)]
if color_level.has_16m { if color_level.has_16m {
AnsiMode::Rgb AnsiMode::Rgb
} else if color_level.has_256 { } else if color_level.has_256 {
AnsiMode::Ansi256 AnsiMode::Ansi256
} else if color_level.has_basic { } else if color_level.has_basic {
// unimplemented!(
// "{mode} color mode not supported",
// mode = AnsiMode::Ansi16.as_ref()
// );
AnsiMode::Ansi256 AnsiMode::Ansi256
} else { } else {
unreachable!(); unreachable!();
@@ -228,9 +277,10 @@ fn create_config(
}); });
debug!(?det_ansi, "detected color mode"); debug!(?det_ansi, "detected color mode");
let asc = get_distro_ascii(distro, backend).context("failed to get distro ascii")?; // Try to get ascii from given backend first, if it fails, try neofetch backend
let asc = get_distro_ascii(distro, backend).or_else(|_| get_distro_ascii(distro, Backend::Neofetch)).context("failed to get distro ascii from neofetch backend")?;
let asc = asc.to_normalized().context("failed to normalize ascii")?; let asc = asc.to_normalized().context("failed to normalize ascii")?;
let theme = det_bg.map(|bg| bg.theme()).unwrap_or(TerminalTheme::Light); let theme = det_bg.map(|bg| bg.theme()).unwrap_or_default();
let color_mode = det_ansi.unwrap_or(AnsiMode::Ansi256); let color_mode = det_ansi.unwrap_or(AnsiMode::Ansi256);
let mut title = format!( let mut title = format!(
"Welcome to {logo} Let's set up some colors first.", "Welcome to {logo} Let's set up some colors first.",
@@ -259,38 +309,19 @@ fn create_config(
.expect("`option_counter` should not overflow `u8`"); .expect("`option_counter` should not overflow `u8`");
} }
fn print_title_prompt( fn print_title_prompt(option_counter: NonZeroU8, prompt: &str) {
option_counter: NonZeroU8, printc!("&a{option_counter}. {prompt}");
prompt: &str,
color_mode: AnsiMode,
) -> Result<()> {
printc(format!("&a{option_counter}. {prompt}"), color_mode)
.context("failed to print prompt")
} }
////////////////////////////// //////////////////////////////
// 0. Check term size // 0. Check term size
{ {
let (Width(term_w), Height(term_h)) = let (Width(term_w), Height(term_h)) = terminal_size().context("failed to get terminal size")?;
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);
let (term_w_min, term_h_min) = (
u16::from(asc.w)
.checked_mul(2)
.unwrap()
.checked_add(4)
.unwrap(),
30,
);
if term_w < term_w_min || term_h < term_h_min { if term_w < term_w_min || term_h < term_h_min {
printc( printc!("&cWarning: Your terminal is too small ({term_w} * {term_h}).\n\
format!( Please resize it to at least ({term_w_min} * {term_h_min}) for better experience.");
"&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")?;
input(Some("Press enter to continue...")).context("failed to read input")?; input(Some("Press enter to continue...")).context("failed to read input")?;
} }
} }
@@ -335,18 +366,14 @@ fn create_config(
(t - a) * ((d - c) / (b - a)) + c (t - a) * ((d - c) / (b - a)) + c
} }
{ let print_color_testing = |label: &str, mode: AnsiMode| {
let label = format!( let label = format!("{label:^term_w$}", term_w = usize::from(term_w));
"{label:^term_w$}",
label = "8bit Color Testing",
term_w = usize::from(term_w)
);
let line = zip(gradient.iter(), label.chars()).fold( let line = zip(gradient.iter(), label.chars()).fold(
String::new(), String::new(),
|mut s, (&rgb_f32_color, t)| { |mut s, (&rgb_f32_color, t)| {
let rgb_u8_color = Srgb::<u8>::from_linear(rgb_f32_color); let rgb_u8_color = Srgb::<u8>::from_linear(rgb_f32_color);
let back = rgb_u8_color let back = rgb_u8_color
.to_ansi_string(AnsiMode::Ansi256, ForegroundBackground::Background); .to_ansi_string(mode, ForegroundBackground::Background);
let fore = rgb_u8_color let fore = rgb_u8_color
.contrast_grayscale() .contrast_grayscale()
.to_ansi_string(AnsiMode::Ansi256, ForegroundBackground::Foreground); .to_ansi_string(AnsiMode::Ansi256, ForegroundBackground::Foreground);
@@ -354,42 +381,15 @@ fn create_config(
s s
}, },
); );
printc(line, AnsiMode::Ansi256).context("failed to print 8-bit color test line")?; printc!("{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")?;
}
writeln!(io::stdout()).context("failed to write to stdout")?; print_color_testing("8bit Color Testing", AnsiMode::Ansi256);
print_title_prompt( print_color_testing("RGB Color Testing", AnsiMode::Rgb);
option_counter,
"Which &bcolor system &ado you want to use?", println!();
color_mode, 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");
.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")?;
let choice = literal_input( let choice = literal_input(
"Your choice?", "Your choice?",
@@ -422,20 +422,14 @@ fn create_config(
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt( print_title_prompt(option_counter, "Is your terminal in &blight mode&~ or &4dark mode&~?");
option_counter,
"Is your terminal in &blight mode&~ or &4dark mode&~?",
color_mode,
)
.context("failed to print title prompt")?;
let choice = literal_input( let choice = literal_input(
"", "",
TerminalTheme::VARIANTS, TerminalTheme::VARIANTS,
TerminalTheme::Dark.as_ref(), TerminalTheme::Dark.as_ref(),
true, true,
color_mode, color_mode,
) )?;
.context("failed to ask for choice input")?;
Ok(( Ok((
choice.parse().expect("selected theme should be valid"), choice.parse().expect("selected theme should be valid"),
"Selected background color", "Selected background color",
@@ -483,28 +477,12 @@ fn create_config(
// Calculate flags per row // Calculate flags per row
let (flags_per_row, rows_per_page) = { let (flags_per_row, rows_per_page) = {
let (Width(term_w), Height(term_h)) = let (Width(term_w), Height(term_h)) = terminal_size().context("failed to get term size")?;
terminal_size().context("failed to get terminal size")?; let flags_per_row = (term_w / (spacing as u16 + 2)).clamp(0, u8::MAX.into()) as u8;
let flags_per_row = term_w.div_euclid(u16::from(spacing).checked_add(2).unwrap()); let rows_per_page = (term_h.saturating_sub(13) / 5).clamp(1, u8::MAX.into()) as u8;
let flags_per_row: u8 = flags_per_row
.try_into()
.expect("`flags_per_row` should fit in `u8`");
let rows_per_page = cmp::max(1, term_h.saturating_sub(13).div_euclid(5));
let rows_per_page: u8 = rows_per_page
.try_into()
.expect("`rows_per_page` should fit in `u8`");
(flags_per_row, rows_per_page) (flags_per_row, rows_per_page)
}; };
let num_pages = let num_pages = (Preset::COUNT.div_ceil(flags_per_row as usize * rows_per_page as usize)).clamp(0, u8::MAX.into()) as u8;
u16::from(u8::try_from(Preset::COUNT).expect("`Preset::COUNT` should fit in `u8`"))
.div_ceil(
u16::from(flags_per_row)
.checked_mul(u16::from(rows_per_page))
.unwrap(),
);
let num_pages: u8 = num_pages
.try_into()
.expect("`num_pages` should fit in `u8`");
// Create pages // Create pages
let mut pages = Vec::with_capacity(usize::from(num_pages)); let mut pages = Vec::with_capacity(usize::from(num_pages));
@@ -522,18 +500,12 @@ fn create_config(
let print_flag_page = |page, page_num: u8| -> Result<()> { let print_flag_page = |page, page_num: u8| -> Result<()> {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; 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) print_title_prompt(option_counter, "Let's choose a flag!");
.context("failed to print title prompt")?; println!("Available flag presets:\nPage: {page_num} of {num_pages}\n", page_num = page_num + 1);
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")?;
for &row in page { for &row in page {
print_flag_row(row, color_mode).context("failed to print flag row")?; print_flag_row(row, color_mode).context("failed to print flag row")?;
} }
writeln!(io::stdout()).context("failed to write to stdout")?; println!();
Ok(()) Ok(())
}; };
@@ -545,7 +517,7 @@ fn create_config(
} }
printc(line.join(" "), color_mode).context("failed to print line")?; printc(line.join(" "), color_mode).context("failed to print line")?;
} }
writeln!(io::stdout()).context("failed to write to stdout")?; println!();
Ok(()) Ok(())
} }
@@ -570,16 +542,9 @@ fn create_config(
let mut opts: Vec<&str> = <Preset as VariantNames>::VARIANTS.into(); let mut opts: Vec<&str> = <Preset as VariantNames>::VARIANTS.into();
opts.extend(["next", "n", "prev", "p"]); opts.extend(["next", "n", "prev", "p"]);
writeln!( println!("Enter '[n]ext' to go to the next page and '[p]rev' to go to the previous page.");
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")?;
let selection = literal_input( let selection = literal_input(
format!( format!("Which {preset_default_colored} do you want to use? "),
"Which {preset} do you want to use? ",
preset = preset_default_colored
),
&opts[..], &opts[..],
Preset::Rainbow.as_ref(), Preset::Rainbow.as_ref(),
false, false,
@@ -628,33 +593,22 @@ fn create_config(
let select_lightness = || -> Result<Lightness> { let select_lightness = || -> Result<Lightness> {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt( print_title_prompt(option_counter, "Let's adjust the color brightness!");
option_counter, println!(
"Let's adjust the color brightness!",
color_mode,
)
.context("failed to print title prompt")?;
writeln!(
io::stdout(),
"The colors might be a little bit too {bright_dark} for {light_dark} mode.\n", "The colors might be a little bit too {bright_dark} for {light_dark} mode.\n",
bright_dark = match theme { bright_dark = match theme {
TerminalTheme::Light => "bright", TerminalTheme::Light => "bright",
TerminalTheme::Dark => "dark", TerminalTheme::Dark => "dark",
}, },
light_dark = theme.as_ref() light_dark = theme.as_ref()
) );
.context("failed to write message to stdout")?;
let color_align = ColorAlignment::Horizontal; let color_align = ColorAlignment::Horizontal;
// Print cats // Print cats
{ {
let (Width(term_w), _) = terminal_size().context("failed to get terminal size")?; let (Width(term_w), _) = terminal_size().context("failed to get terminal size")?;
let num_cols = cmp::max( let num_cols = (term_w / (test_ascii.w as u16 + 2)).clamp(1, u8::MAX.into()) as u8;
1,
term_w.div_euclid(u16::from(test_ascii.w).checked_add(2).unwrap()),
);
let num_cols: u8 = num_cols.try_into().expect("`num_cols` should fit in `u8`");
const MIN: f32 = 0.15; const MIN: f32 = 0.15;
const MAX: f32 = 0.85; const MAX: f32 = 0.85;
let ratios = let ratios =
@@ -705,14 +659,10 @@ fn create_config(
} }
loop { loop {
writeln!( println!("\nWhich brightness level looks the best? (Default: {default:.0}% for {light_dark} mode)",
io::stdout(),
"\nWhich brightness level looks the best? (Default: {default:.0}% for \
{light_dark} mode)",
default = f32::from(default_lightness) * 100.0, default = f32::from(default_lightness) * 100.0,
light_dark = theme.as_ref() light_dark = theme.as_ref()
) );
.context("failed to write prompt to stdout")?;
let lightness = input(Some("> ")) let lightness = input(Some("> "))
.context("failed to read input")? .context("failed to read input")?
.trim() .trim()
@@ -724,12 +674,7 @@ fn create_config(
}, },
Err(err) => { Err(err) => {
debug!(%err, "could not parse lightness"); debug!(%err, "could not parse lightness");
printc( printc!("&cUnable to parse lightness value, please enter a lightness value such as 45%, .45, or 45");
"&cUnable to parse lightness value, please enter a lightness value such \
as 45%, .45, or 45",
color_mode,
)
.context("failed to print message")?;
}, },
} }
} }
@@ -764,33 +709,126 @@ fn create_config(
); );
////////////////////////////// //////////////////////////////
// 5. Color arrangement // 5. Choose Default or Small Logo
let color_align: ColorAlignment;
// Calculate amount of row/column that can be displayed on screen // Calculate amount of row/column that can be displayed on screen
let (ascii_per_row, ascii_rows) = { let (ascii_per_row, ascii_rows) = {
let (Width(term_w), Height(term_h)) = let (Width(term_w), Height(term_h)) = terminal_size().context("failed to get terminal size")?;
terminal_size().context("failed to get terminal size")?; let ascii_per_row = (term_w / (asc.w as u16 + 4)).clamp(1, u8::MAX.into()) as u8;
let ascii_per_row = cmp::max( let ascii_rows = (term_h.saturating_sub(8) / (asc.h as u16 + 1)).clamp(1, u8::MAX.into()) as u8;
1,
term_w.div_euclid(u16::from(asc.w).checked_add(2).unwrap()),
);
let ascii_per_row: u8 = ascii_per_row
.try_into()
.expect("`ascii_per_row` should fit in `u8`");
let ascii_rows = cmp::max(
1,
term_h
.saturating_sub(8)
.div_euclid(u16::from(asc.h).checked_add(1).unwrap()),
);
let ascii_rows: u8 = ascii_rows
.try_into()
.expect("`ascii_rows` should fit in `u8`");
(ascii_per_row, ascii_rows) (ascii_per_row, ascii_rows)
}; };
// 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(),
));
// in case someone specified {distro}_small already in the --distro arg
let detected_dst_small_fmt = if !detected_dst.clone().unwrap().ends_with("_small") {
format!("{}_small", detected_dst.unwrap()).to_lowercase()
} else {
detected_dst.unwrap()
};
let running_dst_sml = if Distro::detect(&detected_dst_small_fmt).is_some() {
detected_dst_small_fmt
} else {
"".to_string()
};
// load ascii
let small_asc = get_distro_ascii(Some(&running_dst_sml), backend).context("failed to get distro ascii")?;
let small_asc = small_asc.to_normalized().context("failed to normalize ascii")?;
let mut asc = asc;
let mut logo_chosen: Option<String> = distro.cloned();
if small_asc.lines != asc.lines && running_dst_sml != "" {
let ds_arrangements = [
("Default", asc.clone()),
("Small", small_asc.clone())
];
let arrangements: IndexMap<Cow<str>, NormalizedAsciiArt> =
ds_arrangements.map(|(k, a)| (k.into(), a)).into();
loop {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
let asciis: Vec<Vec<String>> = arrangements
.iter()
.map(|(k, a)| {
let mut v: Vec<String> = a
.to_recolored(&ColorAlignment::Horizontal, &color_profile, color_mode, theme)
.context("failed to recolor ascii")?
.lines;
if k == "Small" {
// vertical center
let pad_len = (asc.h as usize - v.len()) / 2;
let mut pad_v: Vec<String> = vec![];
for _ in 0..pad_len {
pad_v.push("".to_string());
}
v.splice(0..0, pad_v.clone());
v.extend(pad_v);
let pad_diff = asc.h as usize - v.len();
v.extend(std::iter::repeat("".to_string()).take(pad_diff));
v.push(format!("{k:^asc_width$}", asc_width = usize::from(small_asc.w)));
return Ok(v);
}
v.push(format!("{k:^asc_width$}", asc_width = usize::from(asc.w)));
Ok(v)
})
.collect::<Result<_>>()?;
// prints small logo w/ big logo
for row in &asciis.into_iter().chunks(usize::from(ascii_per_row)) {
let row: Vec<Vec<String>> = row.collect();
for i in 0..usize::from(asc.h).checked_add(1).unwrap() {
let mut line = Vec::new();
for lines in &row {
line.push(&*lines[i]);
}
printc(line.join(" "), color_mode).context("failed to print ascii line")?;
}
println!();
}
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")
.context("failed to select logo type").context("failed to ask for choice input")?;
if choice.to_lowercase() == "small" {
logo_chosen = Some(running_dst_sml);
asc = small_asc;
}
update_title(
&mut title,
&mut option_counter,
"Selected logo type",
choice.as_ref(),
);
break;
}
}
//////////////////////////////
// 6. Color arrangement
let color_align: ColorAlignment;
// Displays horizontal and vertical arrangements in the first iteration, but // Displays horizontal and vertical arrangements in the first iteration, but
// hide them in later iterations // hide them in later iterations
let hv_arrangements = [ let hv_arrangements = [
@@ -821,24 +859,16 @@ fn create_config(
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
// Random color schemes // Random color schemes
let mut preset_indices: Vec<PresetIndexedColor> = let mut preset_indices: Vec<PresetIndexedColor> = (0..color_profile.unique_colors().colors.len()).map(|x| (x as u8).into()).collect();
(0..color_profile.unique_colors().colors.len())
.map(|pi| u8::try_from(pi).expect("`pi` should fit in `u8`").into())
.collect();
while preset_indices.len() < slots.len() { while preset_indices.len() < slots.len() {
preset_indices.extend_from_within(0..); preset_indices.extend_from_within(0..);
} }
let preset_index_permutations: IndexSet<Vec<PresetIndexedColor>> = preset_indices let preset_index_permutations: IndexSet<Vec<PresetIndexedColor>> = preset_indices
.into_iter() .into_iter()
.permutations(slots.len()) .permutations(slots.len())
.take(1000)
.collect(); .collect();
let random_count = u16::from(ascii_per_row) let random_count = (ascii_per_row as usize * ascii_rows as usize).saturating_sub(arrangements.len()).clamp(0, u8::MAX.into()) as u8;
.checked_mul(u16::from(ascii_rows))
.unwrap()
.saturating_sub(u8::try_from(arrangements.len()).unwrap().into());
let random_count: u8 = random_count
.try_into()
.expect("`random_count` should fit in `u8`");
let choices: IndexSet<Vec<PresetIndexedColor>> = let choices: IndexSet<Vec<PresetIndexedColor>> =
if usize::from(random_count) > preset_index_permutations.len() { if usize::from(random_count) > preset_index_permutations.len() {
preset_index_permutations preset_index_permutations
@@ -885,23 +915,13 @@ fn create_config(
for lines in &row { for lines in &row {
line.push(&*lines[i]); line.push(&*lines[i]);
} }
printc(line.join(" "), color_mode).context("failed to print ascii line")?; printc(line.join(" "), color_mode).context("failed to print ascii line")?;
} }
writeln!(io::stdout()).context("failed to write to stdout")?; println!();
} }
print_title_prompt( print_title_prompt(option_counter, "Let's choose a color arrangement!");
option_counter, 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'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")?;
let mut opts: Vec<Cow<str>> = ["horizontal", "vertical", "roll"].map(Into::into).into(); let mut opts: Vec<Cow<str>> = ["horizontal", "vertical", "roll"].map(Into::into).into();
opts.extend((0..random_count).map(|i| format!("random{i}").into())); opts.extend((0..random_count).map(|i| format!("random{i}").into()));
let choice = literal_input("Your choice?", &opts[..], "horizontal", true, color_mode) let choice = literal_input("Your choice?", &opts[..], "horizontal", true, color_mode)
@@ -936,46 +956,31 @@ fn create_config(
); );
////////////////////////////// //////////////////////////////
// 6. Select *fetch backend // 7. Select *fetch backend
let select_backend = || -> Result<Backend> { let select_backend = || -> Result<Backend> {
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
print_title_prompt(option_counter, "Select a *fetch backend", color_mode) print_title_prompt(option_counter, "Select a *fetch backend");
.context("failed to print title prompt")?;
// Check if fastfetch is installed // Check if fastfetch is installed
let fastfetch_path = fastfetch_path().ok(); let fastfetch_path = fastfetch_path().ok();
// Check if macchina is installed // Check if macchina is installed
#[cfg(feature = "macchina")] #[cfg(feature = "macchina")]
let macchina_path = macchina_path().context("failed to get macchina path")?; let macchina_path = macchina_path().unwrap_or(None);
printc( printc!("- &bneofetch&r: Written in bash, &nbest compatibility&r on Unix systems");
"- &bneofetch&r: Written in bash, &nbest compatibility&r on Unix systems", printc!("- &bfastfetch&r: Written in C, &nbest performance&r {}",
color_mode, fastfetch_path
) .map(|path| format!("&a(Installed at {path})", path = path.display()))
.context("failed to print message")?; .unwrap_or_else(|| "&c(Not installed)".to_owned())
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")?;
#[cfg(feature = "macchina")] #[cfg(feature = "macchina")]
printc( printc!("- &bmacchina&r: Written in Rust, &nbest performance&r {}\n",
format!( macchina_path
"- &bmacchina&r: Written in Rust, &nbest performance&r {installed_not_installed}\n", .map(|path| format!("&a(Installed at {path})", path = path.display()))
installed_not_installed = macchina_path .unwrap_or_else(|| "&c(Not installed)".to_owned())
.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")?;
let choice = literal_input( let choice = literal_input(
"Your choice?", "Your choice?",
@@ -996,18 +1001,66 @@ fn create_config(
backend.as_ref(), 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 // Create config
clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?; clear_screen(Some(&title), color_mode, debug_mode).context("failed to clear screen")?;
let config = Config { let config = Config {
preset, preset: preset.as_ref().to_string(),
mode: color_mode, mode: color_mode,
light_dark: theme, light_dark: Some(theme),
auto_detect_light_dark: Some(det_bg.is_some()),
lightness: Some(lightness), lightness: Some(lightness),
color_align, color_align,
backend, backend,
args: None, args: None,
distro: distro.cloned(), distro: logo_chosen,
pride_month_disable: false, pride_month_disable: false,
custom_ascii_path,
}; };
debug!(?config, "created config"); debug!(?config, "created config");
+10 -27
View File
@@ -8,7 +8,7 @@ use bpaf::ShellComp;
use bpaf::{construct, long, OptionParser, Parser as _}; use bpaf::{construct, long, OptionParser, Parser as _};
use directories::BaseDirs; use directories::BaseDirs;
use itertools::Itertools as _; use itertools::Itertools as _;
use strum::{VariantArray, VariantNames}; use strum::VariantNames;
use crate::color_util::{color, Lightness}; use crate::color_util::{color, Lightness};
use crate::presets::Preset; use crate::presets::Preset;
@@ -18,7 +18,7 @@ use crate::types::{AnsiMode, Backend};
pub struct Options { pub struct Options {
pub config: bool, pub config: bool,
pub config_file: PathBuf, pub config_file: PathBuf,
pub preset: Option<Preset>, pub preset: Option<String>,
pub mode: Option<AnsiMode>, pub mode: Option<AnsiMode>,
pub backend: Option<Backend>, pub backend: Option<Backend>,
pub args: Option<Vec<String>>, pub args: Option<Vec<String>>,
@@ -31,6 +31,7 @@ pub struct Options {
pub print_font_logo: bool, pub print_font_logo: bool,
pub test_print: bool, pub test_print: bool,
pub ask_exit: bool, pub ask_exit: bool,
pub auto_detect_light_dark: Option<bool>,
} }
pub fn options() -> OptionParser<Options> { pub fn options() -> OptionParser<Options> {
@@ -54,7 +55,7 @@ pub fn options() -> OptionParser<Options> {
let preset = long("preset") let preset = long("preset")
.short('p') .short('p')
.help(&*format!( .help(&*format!(
"Use preset "Use preset or comma-separated color list or comma-separated hex colors (e.g., \"#ff0000,#00ff00,#0000ff\")
PRESET={{{presets}}}", PRESET={{{presets}}}",
presets = <Preset as VariantNames>::VARIANTS presets = <Preset as VariantNames>::VARIANTS
.iter() .iter()
@@ -64,30 +65,7 @@ PRESET={{{presets}}}",
.argument::<String>("PRESET"); .argument::<String>("PRESET");
#[cfg(feature = "autocomplete")] #[cfg(feature = "autocomplete")]
let preset = preset.complete(complete_preset); let preset = preset.complete(complete_preset);
let preset = preset let preset = preset.optional();
.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 mode = long("mode") let mode = long("mode")
.short('m') .short('m')
.help(&*format!( .help(&*format!(
@@ -170,6 +148,10 @@ BACKEND={{{backends}}}",
.help("Ask for input before exiting") .help("Ask for input before exiting")
.switch() .switch()
.hide(); .hide();
let auto_detect_light_dark = long("auto-detect-light-dark")
.help("Enables hyfetch to detect light/dark terminal background in runtime")
.argument("BOOL")
.optional();
construct!(Options { construct!(Options {
config, config,
@@ -188,6 +170,7 @@ BACKEND={{{backends}}}",
// hidden // hidden
test_print, test_print,
ask_exit, ask_exit,
auto_detect_light_dark,
}) })
.to_options() .to_options()
.header( .header(
+9 -7
View File
@@ -403,18 +403,20 @@ where
Ok(dst) 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. /// Prints with color.
pub fn printc<S>(msg: S, mode: AnsiMode) -> Result<()> pub fn printc<S>(msg: S, mode: AnsiMode) -> Result<()>
where where
S: AsRef<str>, S: AsRef<str>,
{ {
writeln!( println!("{msg}", msg = color(format!("{msg}&r", msg = msg.as_ref()), mode).context("failed to color message")?);
io::stdout(), Ok(())
"{msg}",
msg = color(format!("{msg}&r", msg = msg.as_ref()), mode)
.context("failed to color message")?
)
.context("failed to write message to stdout")
} }
/// Clears screen using ANSI escape codes. /// Clears screen using ANSI escape codes.
+2 -7
View File
@@ -3,10 +3,10 @@ use crate::types::Backend;
use crate::utils::get_cache_path; use crate::utils::get_cache_path;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{self, File}; use std::fs::{File};
use std::io::{Read, Write}; use std::io::{Read, Write};
const FONT_LOGOS: &str = include_str!("../../../hyfetch/data/font_logos.json"); const FONT_LOGOS: &str = include_str!(concat!(env!("OUT_DIR"), "/hyfetch/data/font_logos.json"));
pub fn get_font_logo(backend: Backend) -> Result<String> { pub fn get_font_logo(backend: Backend) -> Result<String> {
// Check if the cache file exists and return its contents if it does // Check if the cache file exists and return its contents if it does
@@ -34,11 +34,6 @@ pub fn get_font_logo(backend: Backend) -> Result<String> {
let logo = font_logos.get(matched_distro).unwrap(); let logo = font_logos.get(matched_distro).unwrap();
// Create parent directories for the cache if they don't exist
if let Some(parent) = cache_path.parent() {
fs::create_dir_all(parent).context("Failed to create cache directory")?;
}
// Write the logo to the cache file // Write the logo to the cache file
let mut cache_file = File::create(cache_path).context("Failed to create cache file")?; let mut cache_file = File::create(cache_path).context("Failed to create cache file")?;
cache_file.write_all(logo.as_bytes()).context("Failed to write logo to cache file")?; cache_file.write_all(logo.as_bytes()).context("Failed to write logo to cache file")?;
+4 -8
View File
@@ -2,14 +2,14 @@ use serde::{Deserialize, Serialize};
use crate::color_util::Lightness; use crate::color_util::Lightness;
use crate::neofetch_util::ColorAlignment; use crate::neofetch_util::ColorAlignment;
use crate::presets::Preset;
use crate::types::{AnsiMode, Backend, TerminalTheme}; use crate::types::{AnsiMode, Backend, TerminalTheme};
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config { pub struct Config {
pub preset: Preset, pub preset: String,
pub mode: AnsiMode, pub mode: AnsiMode,
pub light_dark: TerminalTheme, pub auto_detect_light_dark: Option<bool>,
pub light_dark: Option<TerminalTheme>,
pub lightness: Option<Lightness>, pub lightness: Option<Lightness>,
pub color_align: ColorAlignment, pub color_align: ColorAlignment,
pub backend: Backend, pub backend: Backend,
@@ -18,6 +18,7 @@ pub struct Config {
pub args: Option<Vec<String>>, pub args: Option<Vec<String>>,
pub distro: Option<String>, pub distro: Option<String>,
pub pride_month_disable: bool, pub pride_month_disable: bool,
pub custom_ascii_path: Option<String>,
} }
impl Config { impl Config {
@@ -31,11 +32,6 @@ impl Config {
}, },
} }
} }
pub fn lightness(&self) -> Lightness {
self.lightness
.unwrap_or_else(|| Self::default_lightness(self.light_dark))
}
} }
mod args_serde { mod args_serde {
+13 -38
View File
@@ -3,7 +3,7 @@ use std::ffi::OsStr;
#[cfg(feature = "macchina")] #[cfg(feature = "macchina")]
use std::fs; use std::fs;
use std::io::{self, Write as _}; use std::io::{self, Write as _};
use std::path::{PathBuf}; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::sync::OnceLock; use std::sync::OnceLock;
use std::{env, fmt}; use std::{env, fmt};
@@ -53,7 +53,7 @@ _/\_\_ _/_/\_
pub const NEOFETCH_COLOR_PATTERNS: [&str; 6] = pub const NEOFETCH_COLOR_PATTERNS: [&str; 6] =
["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"]; ["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"];
pub static NEOFETCH_COLORS_AC: OnceLock<AhoCorasick> = OnceLock::new(); pub static NEOFETCH_COLORS_AC: OnceLock<AhoCorasick> = OnceLock::new();
pub const NEOFETCH_SCRIPT: &str = include_str!("../../../neofetch"); pub const NEOFETCH_SCRIPT: &str = include_str!(concat!(env!("OUT_DIR"), "/neofetch"));
#[derive(Clone, Eq, PartialEq, Debug, AsRefStr, Deserialize, Serialize)] #[derive(Clone, Eq, PartialEq, Debug, AsRefStr, Deserialize, Serialize)]
#[serde(tag = "mode")] #[serde(tag = "mode")]
@@ -113,16 +113,12 @@ where
}; };
if let Some(selected) = find_selection(&selection, options) { if let Some(selected) = find_selection(&selection, options) {
writeln!(io::stdout()).context("failed to write to stdout")?; println!();
return Ok(selected); return Ok(selected);
} else { } else {
let options_text = options.iter().map(AsRef::as_ref).join("|"); let options_text = options.iter().map(AsRef::as_ref).join("|");
writeln!( println!("Invalid selection! {selection} is not one of {options_text}");
io::stdout(),
"Invalid selection! {selection} is not one of {options_text}"
)
.context("failed to write message to stdout")?;
} }
} }
@@ -285,7 +281,7 @@ pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec<String>>)
} }
/// Gets distro ascii width and height, ignoring color code. /// 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 where
S: AsRef<str>, S: AsRef<str>,
{ {
@@ -307,25 +303,11 @@ where
return Ok((0, 0)); return Ok((0, 0));
} }
let width = asc let width = asc.lines()
.lines() .map(|line| line.graphemes(true).count()).max()
.map(|line| line.graphemes(true).count())
.max()
.expect("line iterator should not be empty"); .expect("line iterator should not be empty");
let width: u8 = width.try_into().with_context(|| { let width: u16 = width.try_into().context("ascii art width should fit in u16")?;
format!( let height: u16 = asc.lines().count().try_into().context("ascii art height should fit in u16")?;
"`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
)
})?;
Ok((width, height)) Ok((width, height))
} }
@@ -438,6 +420,7 @@ where
debug!(?ff_path, "fastfetch path"); debug!(?ff_path, "fastfetch path");
let mut command = Command::new(ff_path); let mut command = Command::new(ff_path);
command.env("FFTS_IGNORE_PARENT", "1");
command.args(args); command.args(args);
Ok(command) Ok(command)
} }
@@ -481,20 +464,12 @@ where
} }
#[tracing::instrument(level = "debug")] #[tracing::instrument(level = "debug")]
pub(crate) fn get_distro_name(backend: Backend) -> Result<String> { pub fn get_distro_name(backend: Backend) -> Result<String> {
match backend { match backend {
Backend::Neofetch => run_neofetch_command_piped(&["ascii_distro_name"]) Backend::Neofetch => run_neofetch_command_piped(&["ascii_distro_name"])
.context("failed to get distro name from neofetch"), .context("failed to get distro name from neofetch"),
Backend::Fastfetch => run_fastfetch_command_piped(&[ Backend::Fastfetch => Ok(run_fastfetch_command_piped(&["--logo", "none", "-c", "none", "-s", "OS",])
"--logo", .context("failed to get distro name from fastfetch")?.replace("OS: ", "")),
"none",
"-s",
"OS",
"--disable-linewrap",
"--os-key",
" ",
])
.context("failed to get distro name from fastfetch"),
#[cfg(feature = "macchina")] #[cfg(feature = "macchina")]
Backend::Macchina => { Backend::Macchina => {
// Write ascii art to temp file // Write ascii art to temp file
+95 -9
View File
@@ -63,6 +63,8 @@ pub enum Preset {
Aromantic, Aromantic,
Fictosexual,
Aroace1, Aroace1,
Aroace2, Aroace2,
@@ -111,6 +113,10 @@ pub enum Preset {
Genderflux, Genderflux,
Nullflux,
Hypergender, Hyperboy, Hypergirl, Hyperandrogyne, Hyperneutrois,
Finsexual, Finsexual,
Unlabeled1, Unlabeled1,
@@ -165,6 +171,8 @@ pub enum Preset {
Twink, Twink,
Adipophilia,
Kenochoric, Kenochoric,
Veldian, Veldian,
@@ -202,6 +210,11 @@ pub enum Preset {
Neofluid, Neofluid,
Genderqueer, Genderqueer,
Cisgender,
/// Colors from Gilbert Baker's original 1978 flag design
Baker,
/// Meme flag /// Meme flag
Beiyang, Beiyang,
@@ -214,8 +227,12 @@ pub enum Preset {
#[strum(serialize = "throatlozenges")] #[strum(serialize = "throatlozenges")]
ThroatLozenges, ThroatLozenges,
/// Colors from Gilbert Baker's original 1978 flag design /// Meme flag
Baker, Band,
Libragender, Librafeminine, Libramasculine, Libraandrogyne, Libranonbinary,
Fluidfluxa, Fluidfluxb,
} }
#[derive(Clone, Eq, PartialEq, Debug)] #[derive(Clone, Eq, PartialEq, Debug)]
@@ -299,6 +316,11 @@ impl Preset {
"#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000", "#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/ // sourced from https://flag.library.lgbt/flags/aroace/
Self::Aroace1 => ColorProfile::from_hex_colors(vec![ Self::Aroace1 => ColorProfile::from_hex_colors(vec![
"#E28C00", "#ECCD00", "#FFFFFF", "#62AEDC", "#203856", "#E28C00", "#ECCD00", "#FFFFFF", "#62AEDC", "#203856",
@@ -428,6 +450,30 @@ impl Preset {
"f47694", "f2a2b9", "cecece", "7ce0f7", "3ecdf9", "fff48d", "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 // sourced from https://lgbtqia.wiki/wiki/Finsexual
Self::Finsexual => ColorProfile::from_hex_colors(vec![ Self::Finsexual => ColorProfile::from_hex_colors(vec![
"#B18EDF", "#D7B1E2", "#F7CDE9", "#F39FCE", "#EA7BB3", "#B18EDF", "#D7B1E2", "#F7CDE9", "#F39FCE", "#EA7BB3",
@@ -538,6 +584,11 @@ impl Preset {
// colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg // colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg
Self::Twink => ColorProfile::from_hex_colors(vec!["#FFB2FF", "#FFFFFF", "#FFFF81"]), 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 => { Self::Kenochoric => {
ColorProfile::from_hex_colors(vec!["#000000", "#2E1569", "#824DB7", "#C7A1D6"]) ColorProfile::from_hex_colors(vec!["#000000", "#2E1569", "#824DB7", "#C7A1D6"])
}, },
@@ -611,9 +662,18 @@ impl Preset {
"#FFECA0", "#FFFFFF", "#FFECA0", "#38087A", "#BCEC64", "#FFECA0", "#FFFFFF", "#FFECA0", "#38087A", "#BCEC64",
]), ]),
Self::Genderqueer => { Self::Genderqueer => ColorProfile::from_hex_colors(vec![
ColorProfile::from_hex_colors(vec!["#B57EDC", "#FFFFFF", "#4A8123"]) "#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![ Self::Beiyang => ColorProfile::from_hex_colors(vec![
"#DF1B12", "#FFC600", "#01639D", "#FFFFFF", "#000000", "#DF1B12", "#FFC600", "#01639D", "#FFFFFF", "#000000",
@@ -626,11 +686,37 @@ impl Preset {
Self::ThroatLozenges => ColorProfile::from_hex_colors(vec![ Self::ThroatLozenges => ColorProfile::from_hex_colors(vec![
"#2759DA", "#03940D", "#F5F100", "#F59B00", "#B71212", "#2759DA", "#03940D", "#F5F100", "#F59B00", "#B71212",
]), ]),
Self::Band => ColorProfile::from_hex_colors(vec![
"#2670C0", "#F5BD00", "#DC0045", "#E0608E"
]),
// used https://gilbertbaker.com/rainbow-flag-color-meanings/ as source and colorpicked Self::Libragender => ColorProfile::from_hex_colors(vec![
Self::Baker => ColorProfile::from_hex_colors(vec![ "#000000", "#808080", "#92D8E9", "#FFF544", "#FFB0CA", "#808080", "#000000"
"#F23D9E", "#F80A24", "#F78022", "#F9E81F", "#1E972E", "#1B86BC", "#243897", ]),
"#6F0A82",
Self::Librafeminine => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#C6568F", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Libramasculine => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#56C5C5", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Libraandrogyne => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#9186B1", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Libranonbinary => ColorProfile::from_hex_colors(vec![
"#000000", "#A3A3A3", "#FFFFFF", "#FFF987", "#FFFFFF", "#A3A3A3", "#000000"
]),
Self::Fluidfluxa => ColorProfile::from_hex_colors(vec![
"#ff115f", "#a34aa3", "#00a4e7", "#ffdf00", "#000000", "#ffed71", "#85daff", "#dbadda", "#fe8db1"
]),
Self::Fluidfluxb => ColorProfile::from_hex_colors(vec![
"#c6d1d2", "#f47b9d", "#f09f9b", "#e3f09e", "#75eeea", "#52d2ed", "#c6d1d2"
]), ]),
}) })
.expect("preset color profiles should be valid") .expect("preset color profiles should be valid")
+28 -86
View File
@@ -47,58 +47,32 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
}; };
let text = &TEXT_ASCII[1..TEXT_ASCII.len().checked_sub(1).unwrap()]; let text = &TEXT_ASCII[1..TEXT_ASCII.len().checked_sub(1).unwrap()];
let (text_width, text_height) = let (text_w, text_h) = ascii_size(text)?;
ascii_size(text).expect("text ascii should have valid width and height");
let (text, text_width, text_height) = { let (text, text_width, text_height) = {
const TEXT_BORDER_WIDTH: u16 = 2; const TEXT_BORDER_WIDTH: u16 = 2;
const NOTICE_BORDER_WIDTH: u16 = 1; const NOTICE_BORDER_WIDTH: u16 = 1;
const VERTICAL_MARGIN: u16 = 1; const VERTICAL_MARGIN: u16 = 1;
let notice_w = NOTICE.len(); let notice_w: u16 = NOTICE.len().try_into()?;
let notice_w: u8 = notice_w let notice_h: u16 = NOTICE.lines().count().try_into()?;
.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 term_w_min = cmp::max( let term_w_min = cmp::max(
u16::from(text_width) text_w + TEXT_BORDER_WIDTH * 2,
.checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap()) notice_w + NOTICE_BORDER_WIDTH * 2,
.unwrap(),
u16::from(notice_w)
.checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap())
.unwrap(),
); );
let term_h_min = u16::from(text_height) let term_h_min = u16::from(text_h) + notice_h + VERTICAL_MARGIN * 2;
.checked_add(notice_h.into())
.unwrap()
.checked_add(VERTICAL_MARGIN.checked_mul(2).unwrap())
.unwrap();
if w.get() >= term_w_min && h.get() >= term_h_min { if w.get() >= term_w_min && h.get() >= term_h_min {
(text, text_width, text_height) (text, text_w, text_h)
} else { } else {
let text = &TEXT_ASCII_SMALL[1..TEXT_ASCII_SMALL.len().checked_sub(1).unwrap()]; let text = &TEXT_ASCII_SMALL[1..TEXT_ASCII_SMALL.len().checked_sub(1).unwrap()];
let (text_width, text_height) = let (text_w, text_h) = ascii_size(text)?;
ascii_size(text).expect("text ascii should have valid width and height");
let term_w_min = cmp::max( let term_w_min = cmp::max(
u16::from(text_width) text_w + TEXT_BORDER_WIDTH * 2,
.checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap()) notice_w + NOTICE_BORDER_WIDTH * 2,
.unwrap(),
u16::from(notice_w)
.checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap())
.unwrap(),
); );
let term_h_min = u16::from(text_height) let term_h_min = text_h + notice_h + VERTICAL_MARGIN * 2;
.checked_add(notice_h.into())
.unwrap()
.checked_add(VERTICAL_MARGIN.checked_mul(2).unwrap())
.unwrap();
if w.get() < term_w_min || h.get() < term_h_min { if w.get() < term_w_min || h.get() < term_h_min {
return Err(anyhow!( return Err(anyhow!("terminal size should be at least ({term_w_min} * {term_h_min})"));
"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(); let text_lines: Vec<&str> = text.lines().collect();
@@ -165,8 +139,7 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.rem_euclid(colors.len())] .rem_euclid(colors.len())]
.to_ansi_string(color_mode, ForegroundBackground::Background), .to_ansi_string(color_mode, ForegroundBackground::Background),
fg = fg.to_ansi_string(color_mode, ForegroundBackground::Foreground) fg = fg.to_ansi_string(color_mode, ForegroundBackground::Foreground)
) )?;
.unwrap();
// Loop over the width // Loop over the width
for x in 0..w.get() { 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); .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 y_text = text_start_y <= y && y < text_end_y;
let border = 1u16 let border = 1u16 + if y == text_start_y || y == (text_end_y - 1) { 0 } else { 1 };
.checked_add( let text_bounds_x1 = text_start_x - border;
if y == text_start_y || y == text_end_y.checked_sub(1).unwrap() { let text_bounds_x2 = text_end_x - border;
0 let notice_bounds_x1 = notice_start_x - 1;
} else { let notice_bounds_x2 = notice_end_x - 1;
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`");
// If it's a switching point // If it's a switching point
if idx.rem_euclid(NonZeroUsize::from(block_width).get()) == 0 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: LinSrgba = c.with_alpha(1.0).into_linear();
let c = Srgb::<u8>::from_linear(c.overlay(black).without_alpha()); let c = Srgb::<u8>::from_linear(c.overlay(black).without_alpha());
write!( write!(buf, "{bg}", bg = c.to_ansi_string(color_mode, ForegroundBackground::Background))?;
buf,
"{bg}",
bg = c.to_ansi_string(color_mode, ForegroundBackground::Background),
)
.unwrap();
} else { } else {
write!( write!(buf, "{bg}", bg = c.to_ansi_string(color_mode, ForegroundBackground::Background))?;
buf,
"{bg}",
bg = c.to_ansi_string(color_mode, ForegroundBackground::Background),
)
.unwrap();
} }
} }
@@ -240,8 +187,7 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.chars() .chars()
.nth(usize::from(x.checked_sub(text_start_x).unwrap())) .nth(usize::from(x.checked_sub(text_start_x).unwrap()))
.unwrap(), .unwrap(),
) )?;
.unwrap();
} else if y == notice_y && notice_start_x <= x && x < notice_end_x { } else if y == notice_y && notice_start_x <= x && x < notice_end_x {
write!( write!(
buf, buf,
@@ -250,21 +196,15 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
.chars() .chars()
.nth(usize::from(x.checked_sub(notice_start_x).unwrap())) .nth(usize::from(x.checked_sub(notice_start_x).unwrap()))
.unwrap(), .unwrap(),
) )?;
.unwrap();
} else { } else {
write!(buf, " ").unwrap(); write!(buf, " ")?;
} }
} }
// New line if it isn't the last line // New line if it isn't the last line
if y != h.get().checked_sub(1).unwrap() { if y != h.get().checked_sub(1).unwrap() {
writeln!( writeln!(buf, "{reset}", reset = color("&r", color_mode)?)?;
buf,
"{reset}",
reset = color("&r", color_mode).expect("reset should be valid"),
)
.unwrap();
} }
} }
@@ -307,11 +247,13 @@ pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
let mut frame: Wrapping<usize> = Wrapping(0); let mut frame: Wrapping<usize> = Wrapping(0);
const SPEED: u8 = 2; const SPEED: u8 = 2;
let frame_delay = Duration::from_secs_f32(1.0 / 25.0); let frame_delay = Duration::from_secs_f32(2.0 / 25.0);
execute!(io::stdout(), EnterAlternateScreen).context("failed to enter alternate screen")?; execute!(io::stdout(), EnterAlternateScreen).context("failed to enter alternate screen")?;
loop { loop {
// Move cursor to the top left corner
print!("\x1B[H");
draw_frame(frame.0)?; draw_frame(frame.0)?;
frame += usize::from(SPEED); frame += usize::from(SPEED);
thread::sleep(frame_delay); thread::sleep(frame_delay);
+6
View File
@@ -36,6 +36,12 @@ pub enum TerminalTheme {
Dark, Dark,
} }
impl Default for TerminalTheme {
fn default() -> Self {
Self::Dark
}
}
#[derive( #[derive(
Copy, Copy,
Clone, Clone,
+6
View File
@@ -16,6 +16,12 @@ pub fn get_cache_path() -> Result<PathBuf> {
.context("failed to get base dirs")? .context("failed to get base dirs")?
.cache_dir() .cache_dir()
.to_owned(); .to_owned();
// Make sure the cache directory exists
if !path.exists() {
fs::create_dir_all(&path).with_context(|| format!("failed to create cache dir {path:?}"))?;
}
Ok(path) Ok(path)
} }
+38 -37
View File
@@ -1,78 +1,79 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH VERSION "1" "October 2024" "Version is 1.99.0" "User Commands" .TH VERSION: "1" "September 2025" "Version: 2.0.2" "User Commands"
.SH NAME .SH NAME
Version \- manual page for Version is 1.99.0 Version: \- manual page for Version: 2.0.2
.SH SYNOPSIS
.B hyfetch
[\fI\,-c\/\fR] [\fI\,-C=CONFIG_FILE\/\fR] [\fI\,-p=PRESET\/\fR] [\fI\,-m=MODE\/\fR] [\fI\,-b=BACKEND\/\fR] [\fI\,--args=ARGS\/\fR] [\fI\,--c-scale=\/\fR
.SH DESCRIPTION .SH DESCRIPTION
usage: hyfetch [\-h] [\-c] [\-C CONFIG_FILE] SCALE] [\-\-c\-set\-l=LIGHTNESS] [\-\-june] [\-\-debug] ([\-\-distro=DISTRO] | [\-\-test\-distro=DISTRO]) [
.IP \fB\-\-ascii\-file\fR=\fI\,ASCII_FILE]\/\fR [\-\-print\-font\-logo] [\-\-auto\-detect\-light\-dark=BOOL]
[\-p {rainbow,transgender,nonbinary,xenogender,agender,queer,genderfluid,bisexual,pansexual,polysexual,omnisexual,omniromantic,gay\-men,lesbian,abrosexual,asexual,aromantic,aroace1,aroace2,aroace3,autosexual,intergender,greygender,akiosexual,bigender,demigender,demiboy,demigirl,transmasculine,transfeminine,genderfaun,demifaun,genderfae,demifae,neutrois,biromantic1,biromantic2,autoromantic,boyflux2,girlflux,genderflux,finsexual,unlabeled1,unlabeled2,pangender,pangender.contrast,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,plural,fraysexual,bear,butch,leather,otter,twink,kenochoric,veldian,solian,lunian,polyam,sapphic,androgyne,interprogress,progress,intersex,old\-polyam,equal\-rights,drag,pronounfluid,pronounflux,exipronoun,neopronoun,neofluid,genderqueer,beiyang,burger,baker,caninekin,random}]
[\-m {8bit,rgb}]
[\-b {qwqfetch,neofetch,fastfetch,fastfetch\-old}] [\-\-args ARGS]
[\-\-c\-scale SCALE] [\-\-c\-set\-l LIGHT] [\-\-c\-overlay] [\-V] [\-\-june]
[\-\-debug] [\-\-distro DISTRO] [\-\-ascii\-file ASCII_FILE]
[\-\-print\-font\-logo]
.PP .PP
hyfetch \- neofetch with flags <3 hyfetch \- neofetch with flags <3
.SS "options:" .SS "Available options:"
.TP
\fB\-h\fR, \fB\-\-help\fR
show this help message and exit
.TP .TP
\fB\-c\fR, \fB\-\-config\fR \fB\-c\fR, \fB\-\-config\fR
Configure hyfetch Configure hyfetch
.TP .TP
\fB\-C\fR CONFIG_FILE, \fB\-\-config\-file\fR CONFIG_FILE \fB\-C\fR, \fB\-\-config\-file\fR=\fI\,CONFIG_FILE\/\fR
Use another config file Use another config file
[default: "/home/azalea/.config/hyfetch.json"]
.TP .TP
\fB\-p\fR {rainbow,transgender,nonbinary,xenogender,agender,queer,genderfluid,bisexual,pansexual,polysexual,omnisexual,omniromantic,gay\-men,lesbian,abrosexual,asexual,aromantic,aroace1,aroace2,aroace3,autosexual,intergender,greygender,akiosexual,bigender,demigender,demiboy,demigirl,transmasculine,transfeminine,genderfaun,demifaun,genderfae,demifae,neutrois,biromantic1,biromantic2,autoromantic,boyflux2,girlflux,genderflux,finsexual,unlabeled1,unlabeled2,pangender,pangender.contrast,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,plural,fraysexual,bear,butch,leather,otter,twink,kenochoric,veldian,solian,lunian,polyam,sapphic,androgyne,interprogress,progress,intersex,old\-polyam,equal\-rights,drag,pronounfluid,pronounflux,exipronoun,neopronoun,neofluid,genderqueer,beiyang,burger,baker,caninekin,random}, \fB\-\-preset\fR {rainbow,transgender,nonbinary,xenogender,agender,queer,genderfluid,bisexual,pansexual,polysexual,omnisexual,omniromantic,gay\-men,lesbian,abrosexual,asexual,aromantic,aroace1,aroace2,aroace3,autosexual,intergender,greygender,akiosexual,bigender,demigender,demiboy,demigirl,transmasculine,transfeminine,genderfaun,demifaun,genderfae,demifae,neutrois,biromantic1,biromantic2,autoromantic,boyflux2,girlflux,genderflux,finsexual,unlabeled1,unlabeled2,pangender,pangender.contrast,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,plural,fraysexual,bear,butch,leather,otter,twink,kenochoric,veldian,solian,lunian,polyam,sapphic,androgyne,interprogress,progress,intersex,old\-polyam,equal\-rights,drag,pronounfluid,pronounflux,exipronoun,neopronoun,neofluid,genderqueer,beiyang,burger,baker,caninekin,random} \fB\-p\fR, \fB\-\-preset\fR=\fI\,PRESET\/\fR
Use preset 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}
.TP .TP
\fB\-m\fR {8bit,rgb}, \fB\-\-mode\fR {8bit,rgb} \fB\-m\fR, \fB\-\-mode\fR=\fI\,MODE\/\fR
Color mode Color mode MODE={8bit,rgb}
.TP .TP
\fB\-b\fR {qwqfetch,neofetch,fastfetch,fastfetch\-old}, \fB\-\-backend\fR {qwqfetch,neofetch,fastfetch,fastfetch\-old} \fB\-b\fR, \fB\-\-backend\fR=\fI\,BACKEND\/\fR
Choose a *fetch backend Choose a *fetch backend BACKEND={neofetch,fastfetch,macchina}
.TP .TP
\fB\-\-args\fR ARGS \fB\-\-args\fR=\fI\,ARGS\/\fR
Additional arguments pass\-through to backend Additional arguments pass\-through to backend
.TP .TP
\fB\-\-c\-scale\fR SCALE \fB\-\-c\-scale\fR=\fI\,SCALE\/\fR
Lighten colors by a multiplier Lighten colors by a multiplier
.TP .TP
\fB\-\-c\-set\-l\fR LIGHT \fB\-\-c\-set\-l\fR=\fI\,LIGHTNESS\/\fR
Set lightness value of the colors Set lightness value of the colors
.TP .TP
\fB\-\-c\-overlay\fR
Use experimental overlay color adjusting instead of
HSL lightness
.TP
\fB\-V\fR, \fB\-\-version\fR
Check version
.TP
\fB\-\-june\fR \fB\-\-june\fR
Show pride month easter egg Show pride month easter egg
.TP .TP
\fB\-\-debug\fR \fB\-\-debug\fR
Debug mode Debug mode
.TP .TP
\fB\-\-distro\fR DISTRO, \fB\-\-test\-distro\fR DISTRO \fB\-\-distro\fR=\fI\,DISTRO\/\fR
Test for a specific distro Test for a specific distro
.TP .TP
\fB\-\-ascii\-file\fR ASCII_FILE \fB\-\-test\-distro\fR=\fI\,DISTRO\/\fR
Test for a specific distro
.TP
\fB\-\-ascii\-file\fR=\fI\,ASCII_FILE\/\fR
Use a specific file for the ascii art Use a specific file for the ascii art
.TP .TP
\fB\-\-print\-font\-logo\fR \fB\-\-print\-font\-logo\fR
Print the Font Logo / Nerd Font icon of your distro Print the Font Logo / Nerd Font icon of your distro and exit
and exit .TP
\fB\-\-auto\-detect\-light\-dark\fR=\fI\,BOOL\/\fR
Enables hyfetch to detect light/dark terminal background in
runtime
.TP
\fB\-h\fR, \fB\-\-help\fR
Prints help information
.TP
\fB\-V\fR, \fB\-\-version\fR
Prints version information
.SH "SEE ALSO" .SH "SEE ALSO"
The full documentation for The full documentation for
.B Version .B Version:
is maintained as a Texinfo manual. If the is maintained as a Texinfo manual. If the
.B info .B info
and and
.B Version .B Version:
programs are properly installed at your site, the command programs are properly installed at your site, the command
.IP .IP
.B info Version .B info Version:
.PP .PP
should give you access to the complete manual. should give you access to the complete manual.
+38 -39
View File
@@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH NEOFETCH "1" "October 2024" "Neofetch 7.98.0" "User Commands" .TH NEOFETCH "1" "September 2025" "Neofetch 8.0.2" "User Commands"
.SH NAME .SH NAME
Neofetch \- manual page for Neofetch 7.98.0 Neofetch \- manual page for Neofetch 8.0.2
.SH SYNOPSIS .SH SYNOPSIS
.B neofetch .B neofetch
\fI\,func_name --option "value" --option "value"\/\fR \fI\,func_name --option "value" --option "value"\/\fR
@@ -321,12 +321,12 @@ Amazon, AmogOS, Anarchy, Android, Antergos, antiX, AOSC OS, Aperio
GNU/Linux, Aperture, Apricity, Arch, ArchBox, Archcraft, GNU/Linux, Aperture, Apricity, Arch, ArchBox, Archcraft,
archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike, archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike,
ArcoLinux, Arkane, ArseLinux, Artix, Arya, Asahi, AsteroidOS, astOS, ArcoLinux, Arkane, ArseLinux, Artix, Arya, Asahi, AsteroidOS, astOS,
Astra Linux, Athena, azos, Bedrock, BigLinux, BigLinux_large, Astra Linux, Athena, azos, Bazzite, Bedrock, BigLinux,
Bitrig, BlackArch, BlackMesa, blackPanther, BLAG, BlankOn, BigLinux_large, Bitrig, BlackArch, BlackMesa, blackPanther, BLAG,
BlueLight, Bodhi, bonsai, BSD, BunsenLabs, CachyOS, Calculate, BlankOn, BlueLight, Bodhi, bonsai, BSD, BunsenLabs, CachyOS,
CalinixOS, Carbs, CBL\-Mariner, CelOS, Center, CentOS, Chakra, Calculate, CalinixOS, Carbs, CBL\-Mariner, CelOS, Center, CentOS,
ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro, Clear Chakra, ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro,
Linux OS, ClearOS, Clover, Cobalt, Condres, Container Linux by Clear Linux OS, ClearOS, Clover, Cobalt, Condres, Container Linux by
CoreOS, CRUX, Crystal Linux, Cucumber, CutefishOS, CuteOS, CyberOS, CoreOS, CRUX, Crystal Linux, Cucumber, CutefishOS, CuteOS, CyberOS,
dahlia, DarkOs, Darwin, Debian, Deepin, DesaOS, Devuan, DietPi, dahlia, DarkOs, Darwin, Debian, Deepin, DesaOS, Devuan, DietPi,
digital UNIX, DracOS, DragonFly, Drauger, Droidian, Elementary, digital UNIX, DracOS, DragonFly, Drauger, Droidian, Elementary,
@@ -335,37 +335,36 @@ EvolutionOS, eweOS, Exherbo, Exodia Predator OS, Fedora, Fedora
Kinoite, Fedora Sericea, Fedora Silverblue, Fedora_unicode, Kinoite, Fedora Sericea, Fedora Silverblue, Fedora_unicode,
FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT, Frugalware, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT, Frugalware,
Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus, Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus,
gNewSense, GNOME, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, Gloire, gNewSense, GNOME, GNU, GoboLinux, GrapheneOS, Grombyang,
Haiku, HamoniKR, HarDClanZ, Hash, Huayra, Hybrid, HydroOS, Guix, Haiku, HamoniKR, HarDClanZ, Hash, Huayra, Hybrid, HydroOS,
Hyperbola, iglunix, instantOS, Interix, IRIX, Ironclad, Itc, Hyperbola, iglunix, instantOS, Interix, IRIX, Itc, januslinux,
januslinux, Kaisen, Kali, KaOS, KDE, Kibojoe, Kogaion, Korora, Kaisen, Kali, KaOS, KDE, Kibojoe, Kogaion, Korora, KrassOS, KSLinux,
KrassOS, KSLinux, Kubuntu, LainOS, LangitKetujuh, LaxerOS, LEDE, Kubuntu, LainOS, LangitKetujuh, LaxerOS, LEDE, LibreELEC, Linspire,
LibreELEC, Linspire, Linux, Linux Lite, Linux Mint, Linux Mint Old, Linux, Linux Lite, Linux Mint, Linux Mint Old, LinuxFromScratch,
LinuxFromScratch, Live Raizo, LMDE, Lubuntu, Lunar, mac, MacaroniOS, Live Raizo, LMDE, Lubuntu, Lunar, mac, MacaroniOS, Mageia, Magix,
Mageia, Magix, MagpieOS, MainsailOS, Mandriva, Manjaro, MassOS, MagpieOS, MainsailOS, Mandriva, Manjaro, MassOS, MatuusOS, Maui,
MatuusOS, Maui, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, NekOS, Neptune,
NekOS, Neptune, NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful, NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful, Nobara, NomadBSD,
Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, Open Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, Open Source Media
Source Media Center, OpenBSD, openEuler, OpenIndiana, openKylin, Center, OpenBSD, openEuler, OpenIndiana, openKylin, openmamba,
openmamba, OpenMandriva, OpenStage, openSUSE, openSUSE Leap, OpenMandriva, OpenStage, openSUSE, openSUSE Leap, openSUSE
openSUSE Tumbleweed, OPNsense, Oracle, orchid, OS Elbrus, PacBSD, Tumbleweed, openSUSE Tumbleweed\-Slowroll, OPNsense, Oracle, orchid,
Panwah, Parabola, parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, OS Elbrus, PacBSD, Panwah, Parabola, parch, Pardus, Parrot, Parsix,
pearOS, Pengwin, Pentoo, Peppermint, Peropesis, phyOS, PikaOS, Pisi, PCBSD, PCLinuxOS, pearOS, Pengwin, Pentoo, Peppermint, Peropesis,
PNM Linux, Pop!_OS, Porteus, PostMarketOS, Profelis SambaBOX, phyOS, PikaOS, Pisi, PNM Linux, Pop!_OS, Porteus, PostMarketOS,
Proxmox, PuffOS, Puppy, PureOS, Q4OS, Qubes, Qubyt, Quibian, Radix, Profelis SambaBOX, Proxmox, PuffOS, Puppy, PureOS, Q4OS, Qubes,
Raspbian, ravynOS, Reborn OS, Red Star, Redcore, Redhat, Refracted Qubyt, Quibian, Radix, Raspbian, ravynOS, Reborn OS, Red Star,
Devuan, Regata, Regolith, RhaymOS, rocky, Rosa, Sabayon, sabotage, Redcore, Redhat, Refracted Devuan, Regata, Regolith, RhaymOS, Rhino
Sailfish, SalentOS, Salient OS, Salix, Sasanqua, Scientific, semc, Linux, rocky, Rosa, Sabayon, sabotage, Sailfish, SalentOS, Salient
Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, OS, Salix, Sasanqua, Scientific, semc, Septor, Serene, SharkLinux,
Slackware, SliTaz, SmartOS, Soda, Solus, Source Mage, Sparky, Star, ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SliTaz, SmartOS,
SteamOS, Stock Linux, Sulin, SunOS, SwagArch, t2, Tails, Tatra, Soda, Solus, Source Mage, Sparky, Star, SteamOS, Stock Linux, Sulin,
TeArch, TorizonCore, Trisquel, Twister, Ubuntu, Ubuntu Budgie, SunOS, SwagArch, t2, Tails, Tatra, TeArch, TorizonCore, Trisquel,
Ubuntu Cinnamon, Ubuntu Kylin, Ubuntu MATE, Ubuntu Studio, Ubuntu Twister, Ubuntu, Ubuntu Budgie, Ubuntu Cinnamon, Ubuntu Kylin,
Sway, Ubuntu Touch, Ubuntu\-GNOME, ubuntu_old02, Ultramarine Linux, Ubuntu MATE, Ubuntu Studio, Ubuntu Sway, Ubuntu Touch, Ubuntu\-GNOME,
unicodearch, Univalent, Univention, Uos, UrukOS, uwuntu, Vanilla, ubuntu_old02, Ultramarine Linux, unicodearch, Univalent, Univention,
Venom, VNux, Void, VzLinux, wii\-linux\-ngx, Windows, Windows 10, Uos, UrukOS, uwuntu, Vanilla, Venom, VNux, Void, VzLinux, wii\-linuxngx, Windows, Windows 10, Windows 11, Windows95, Wrt, Xenia, Xenia2,
Windows 11, Windows95, Wrt, Xenia, Xenia2, XFerience, Xray_OS, XFerience, Xray_OS, Xubuntu, yiffOS, Zorin have ascii logos.
Xubuntu, yiffOS, Zorin have ascii logos.
.TP .TP
NOTE: arch, dragonfly, Fedora, LangitKetujuh, nixos, redhat, Ubuntu NOTE: arch, dragonfly, Fedora, LangitKetujuh, nixos, redhat, Ubuntu
have 'old' logo variants, use {distro}_old to use them. have 'old' logo variants, use {distro}_old to use them.
+1 -1
View File
@@ -1 +1 @@
VERSION = '1.99.0' VERSION = '2.0.2'
+11 -3
View File
@@ -135,9 +135,17 @@ class RGB:
:return: RGB object :return: RGB object
""" """
hex = hex.lstrip("#") hex = hex.lstrip("#")
r = int(hex[0:2], 16)
g = int(hex[2:4], 16) if len(hex) == 6:
b = int(hex[4:6], 16) r = int(hex[0:2], 16)
g = int(hex[2:4], 16)
b = int(hex[4:6], 16)
elif len(hex) == 3:
r = int(hex[0], 16)
g = int(hex[1], 16)
b = int(hex[2], 16)
else:
raise ValueError(f"Error: invalid hex length")
return cls(r, g, b) return cls(r, g, b)
def to_ansi_rgb(self, foreground: bool = True) -> str: def to_ansi_rgb(self, foreground: bool = True) -> str:
+3 -1
View File
@@ -3,6 +3,8 @@
"Alpine": "", "Alpine": "",
"AOSC OS": "", "AOSC OS": "",
"Apple": "", "Apple": "",
"macOS": "",
"iOS": "",
"Archcraft": "", "Archcraft": "",
"ArchLabs": "", "ArchLabs": "",
"Arch": "", "Arch": "",
@@ -118,4 +120,4 @@
"Xorg": "", "Xorg": "",
"Zorin OS": "", "Zorin OS": "",
"Windows": "\uE62A" "Windows": "\uE62A"
} }
+26
View File
@@ -0,0 +1,26 @@
# This file is automatically generated. Please do not modify.
from . import AsciiArt
bazzite = AsciiArt(match=r'''"Bazzite"*''', color='5 5', ascii=r"""
${c1} %%%%%%====%%%%%%%%%%
%%%%%%%% %%%%%%%%%%%%%%
%%%%%%%%% %%%%%%%%%%%%%%%%
%%%%%%%%% %%%%%%%%%%%%%%%###
%%%%%%%%% %%%%%%%%%%%%%######
== =======######
== =========#####
%%%%%%%%% %%%%%%%####======#####
%%%%%%%%% %%%%%#######=====#####
%%%%%%%%% %%%#########=====#####
%%%%%%%%% %%##########=====#####
%%%%%%%%%====###########=====######
%%%%%%%%====#########======######
%%%%%%%=====#####========######
%%%%###===============#######
%#######==========#########
#######################
###################
###########
""")
+15 -3
View File
@@ -208,6 +208,10 @@ def detect(name: str) -> AsciiArt | None:
from .bedrock import bedrock from .bedrock import bedrock
return bedrock return bedrock
if name.startswith('bazzite'):
from .bazzite import bazzite
return bazzite
if name.startswith('biglinux_large'): if name.startswith('biglinux_large'):
from .biglinux_large import biglinux_large from .biglinux_large import biglinux_large
return biglinux_large return biglinux_large
@@ -664,9 +668,9 @@ def detect(name: str) -> AsciiArt | None:
from .interix import interix from .interix import interix
return interix return interix
if name.startswith('ironclad'): if name.startswith('gloire'):
from .ironclad import ironclad from .gloire import gloire
return ironclad return gloire
if name.startswith('januslinux') or name.startswith('janus') or name.startswith('ataraxia linux') or name.startswith('ataraxia'): if name.startswith('januslinux') or name.startswith('janus') or name.startswith('ataraxia linux') or name.startswith('ataraxia'):
from .januslinux import januslinux from .januslinux import januslinux
@@ -1180,6 +1184,10 @@ def detect(name: str) -> AsciiArt | None:
from .rhaymos import rhaymos from .rhaymos import rhaymos
return rhaymos return rhaymos
if name.startswith('rhino linux'):
from .rhino_linux import rhino_linux
return rhino_linux
if name.startswith('rocky_small'): if name.startswith('rocky_small'):
from .rocky_small import rocky_small from .rocky_small import rocky_small
return rocky_small return rocky_small
@@ -1312,6 +1320,10 @@ def detect(name: str) -> AsciiArt | None:
from .t2 import t2 from .t2 import t2
return t2 return t2
if name.startswith('opensuse tumbleweed-slowroll') or name.startswith('opensuse_slowroll'):
from .opensuse_tumbleweed_slowroll import opensuse_tumbleweed_slowroll
return opensuse_tumbleweed_slowroll
if name.startswith('opensuse tumbleweed') or name.startswith('opensuse_tumbleweed'): if name.startswith('opensuse tumbleweed') or name.startswith('opensuse_tumbleweed'):
from .opensuse_tumbleweed import opensuse_tumbleweed from .opensuse_tumbleweed import opensuse_tumbleweed
return opensuse_tumbleweed return opensuse_tumbleweed
+19
View File
@@ -0,0 +1,19 @@
# This file is automatically generated. Please do not modify.
from . import AsciiArt
gloire = AsciiArt(match=r'''"Gloire"*''', color='5 7 0', ascii=r"""
${c3}
&#BGPPPPPG#&
B5?77!!?YJJ7!7YBB&
&G5YJ77!7JYYYYYBPJ&PY#
#PYYYYYY?!?YYYYY7?7JP5JJ
B?YYYYYY7!!7JYYYYJ!!?JJJ5
&& B7?J?77?7!!!!!77777!7Y5YYBBPGGG&
G77?YBB!!!!!!!!!!!!!JYJ??7JYJJY# PYPPG&
J777JB?!7JJ???!!!7?JYYYYYPJ!7JB
GYYG #JJJJJ??7!!!JYYY5PGB&GB&
#Y!?GB5YYJY5PG###&
GJJP
""")
+1 -1
View File
@@ -3,7 +3,7 @@
from . import AsciiArt from . import AsciiArt
nixos_small = AsciiArt(match=r'''"nixos_small"''', color='4 6', ascii=r""" nixos_small = AsciiArt(match=r'''"nixos_small"''', color='4 6', ascii=r"""
${c1} \\ \\ // ${c1} \\ \\ //
==\\__\\/ // ==\\__\\/ //
// \\// // \\//
==// //== ==// //==
@@ -0,0 +1,27 @@
# This file is automatically generated. Please do not modify.
from . import AsciiArt
opensuse_tumbleweed_slowroll = AsciiArt(match=r'''"openSUSE Tumbleweed-Slowroll"* | "openSUSE_Slowroll"*''', color='2 7', ascii=r"""
${c2} _aaaymQQmwaaa,
,wWQQQD????????$QQQQa,.
_wQQB?" ??QQQa,
sQQD^ ?QQ6\
yWW' 4QQg
,QQD .aaaaaaaa ^4Q6
,mQP _wWQW?????YWWQa, 4Qm
jQ@ wWW?' ^4QQc ^$QL
,QQ' jWW' )QW\ ]QQ
|QQ ,QW' ]QQ ^QQ|
|QQ |QQ ]QQ QQ|
|QQ 4Qg ]QQ .QQ|
'QQ6 '$WQac. _QQ( jQQ
]QQw "?QWQQf _mQP ,QQ(
4QQga wQQP ,mQ?
4QQQga, saQWP' jQQf
?QQQQQQwaaaaaaaayWWW?' _mQ@'
?WQQQP?9VWUV???^ _amQP^
"4QQQaa, ,awQQQ?^
"?VQQQQQQQQQQQQQQP?'
""")
+27
View File
@@ -0,0 +1,27 @@
# This file is automatically generated. Please do not modify.
from . import AsciiArt
rhino_linux = AsciiArt(match=r'''"Rhino Linux"*''', color='5 4', ascii=r"""
${c1}
${c1} ..','.. l.
.,coooooooooc,..ool
;oooooooooooooooooooo.,coo.
;oooooooooooooooooooooooooo ,
.ooooooooooooooooooooooooooo . ,o
oollcccccccccllloooooooooooolcoo..oo
ll;':::::::::::::::ccloooooooooooooo:
.;''';::::::::::::::::::ccloooooooooo
''''',:::::::::::::::::::::::cclllllc
'''''';::::::::::::::::::::::::::::::
${c1}.'''''';::::::::::::::::${c2}ll${c1}:::::::::::
${c1} ''''''';:::::::::::::::${c2}d0${c1}::
${c1}.''''''',;:::::::${c2}cc${c1}::::${c2}0N:'
${c1}''''''''',,;::::${c2}cK${c1}:::${c2}dMX,
${c1}'''''''''''',,,${c2}dMccOMM${c1}:
${c1}.''''''''''''${c2}cWM0WMM${c1}.
${c1}'''''''':${c2}kMMMMM${c1}.
${c2}odxONMMMM
;Kl
""")
+75 -13
View File
@@ -5,9 +5,10 @@ import argparse
import datetime import datetime
import importlib.util import importlib.util
import json import json
import os
import random import random
import traceback import traceback
from itertools import permutations from itertools import permutations, islice
from math import ceil from math import ceil
from . import termenv, neofetch_util, pride_month from . import termenv, neofetch_util, pride_month
@@ -17,7 +18,7 @@ from .constants import *
from .font_logo import get_font_logo from .font_logo import get_font_logo
from .models import Config from .models import Config
from .neofetch_util import * from .neofetch_util import *
from .presets import PRESETS from .presets import PRESETS, ColorProfile
def check_config(path) -> Config: def check_config(path) -> Config:
@@ -251,7 +252,7 @@ def create_config() -> Config:
slots = list(set(map(int, re.findall('(?<=\\${c)[0-9](?=})', asc)))) slots = list(set(map(int, re.findall('(?<=\\${c)[0-9](?=})', asc))))
while len(pis) < len(slots): while len(pis) < len(slots):
pis += pis pis += pis
perm = {p[:len(slots)] for p in permutations(pis)} perm = {p[:len(slots)] for p in islice(permutations(pis), 1000)}
random_count = max(0, ascii_per_row * ascii_rows - len(arrangements)) random_count = max(0, ascii_per_row * ascii_rows - len(arrangements))
if random_count > len(perm): if random_count > len(perm):
choices = perm choices = perm
@@ -309,7 +310,7 @@ def create_config() -> Config:
printc('- &bqwqfetch&r: Pure python, &nminimal dependencies&r ' + printc('- &bqwqfetch&r: Pure python, &nminimal dependencies&r ' +
('&c(Not installed)' if not has_qwqfetch else '')) ('&c(Not installed)' if not has_qwqfetch else ''))
print() print()
# Use fastfetch as the default backend if it is installed # Use fastfetch as the default backend if it is installed
def_backend = 'neofetch' if ff_path is None else 'fastfetch' def_backend = 'neofetch' if ff_path is None else 'fastfetch'
@@ -318,9 +319,49 @@ def create_config() -> Config:
backend = select_backend() backend = select_backend()
update_title('Selected backend', 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 # Create config
clear_screen(title) 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 # Save config
print() 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', 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('-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('-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('-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') parser.add_argument('--args', help=f'Additional arguments pass-through to backend')
@@ -382,7 +423,7 @@ def run():
GLOBAL_CFG.use_overlay = args.overlay GLOBAL_CFG.use_overlay = args.overlay
if args.version: if args.version:
print(f'Version is {VERSION}') print(f'Version is 1.99.∞\n(python hyfetch legacy mode)')
return return
# Ensure git bash for windows # Ensure git bash for windows
@@ -414,17 +455,18 @@ def run():
# Check if it's June (pride month) # Check if it's June (pride month)
now = datetime.datetime.now() now = datetime.datetime.now()
june_path = CACHE_PATH / f'animation-displayed-{now.year}' june_path = CACHE_PATH / f'animation-displayed-{now.year}'
show_for_june = False
if now.month == 6 and now.year not in config.pride_month_shown and not june_path.is_file() and os.isatty(sys.stdout.fileno()): if now.month == 6 and now.year not in config.pride_month_shown and not june_path.is_file() and os.isatty(sys.stdout.fileno()):
args.june = True show_for_june = True
if args.june and not config.pride_month_disable: if (args.june or show_for_june) and not config.pride_month_disable:
pride_month.start_animation() pride_month.start_animation()
print() print()
print("Happy pride month!") print("Happy pride month!")
print("(You can always view the animation again with `hyfetch --june`)") print("(You can always view the animation again with `hyfetch --june`)")
print() print()
if not june_path.is_file(): if not june_path.is_file() and not args.june: # If --june wasn't explicitly specified...
june_path.parent.mkdir(parents=True, exist_ok=True) june_path.parent.mkdir(parents=True, exist_ok=True)
june_path.touch() june_path.touch()
@@ -440,7 +482,7 @@ def run():
config.backend = args.backend config.backend = args.backend
if args.args: if args.args:
config.args = args.args config.args = args.args
# Random preset # Random preset
if config.preset == 'random': if config.preset == 'random':
config.preset = random.choice(list(PRESETS.keys())) config.preset = random.choice(list(PRESETS.keys()))
@@ -450,7 +492,21 @@ def run():
GLOBAL_CFG.is_light = config.light_dark == 'light' GLOBAL_CFG.is_light = config.light_dark == 'light'
# Get preset # 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) # Lighten (args > config)
if args.scale: if args.scale:
@@ -462,8 +518,14 @@ def run():
# Run # Run
try: 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 = config.color_align.recolor_ascii(asc, preset)
asc = '\n'.join(asc.split('\n')[1:])
neofetch_util.run(asc, config.backend, config.args or '') neofetch_util.run(asc, config.backend, config.args or '')
except Exception as e: except Exception as e:
print(f'Error: {e}') print(f'Error: {e}')
+1
View File
@@ -20,6 +20,7 @@ class Config:
distro: str | None = None distro: str | None = None
pride_month_shown: list[int] = field(default_factory=list) # This is deprecated, see issue #136 pride_month_shown: list[int] = field(default_factory=list) # This is deprecated, see issue #136
pride_month_disable: bool = False pride_month_disable: bool = False
custom_ascii_path: str | None = None
@classmethod @classmethod
def from_dict(cls, d: dict): def from_dict(cls, d: dict):
+5 -3
View File
@@ -333,7 +333,7 @@ def run_qwqfetch(asc: str, args: str = ''):
import qwqfetch import qwqfetch
# distro_detector only return a bash variable # distro_detector only return a bash variable
# so we use qwqfetch builtin distro detector # so we use qwqfetch builtin distro detector
print(qwqfetch.get_ascres(asc)) print(qwqfetch.get_ascres(asc))
except ImportError as e: # module not found etc except ImportError as e: # module not found etc
print("qwqfetch is not installed. Install it by executing:") # use print to output hint directly print("qwqfetch is not installed. Install it by executing:") # use print to output hint directly
print("pip install git+https://github.com/nexplorer-3e/qwqfetch") # TODO: public repo print("pip install git+https://github.com/nexplorer-3e/qwqfetch") # TODO: public repo
@@ -379,17 +379,19 @@ def run_fastfetch(asc: str, args: str = '', legacy: bool = False):
""" """
# Find fastfetch binary # Find fastfetch binary
ff_path = fastfetch_path() ff_path = fastfetch_path()
if not ff_path: if not ff_path:
printc("&cError: fastfetch binary is not found. Please install fastfetch first.") printc("&cError: fastfetch binary is not found. Please install fastfetch first.")
exit(127) exit(127)
# Write temp file # Write temp file
with TemporaryDirectory() as tmp_dir: with TemporaryDirectory() as tmp_dir:
tmp_dir = Path(tmp_dir) tmp_dir = Path(tmp_dir)
path = tmp_dir / 'ascii.txt' path = tmp_dir / 'ascii.txt'
path.write_text(asc, 'utf-8') path.write_text(asc, 'utf-8')
os.environ['FFTS_IGNORE_PARENT'] = '1'
# Call fastfetch with the temp file # Call fastfetch with the temp file
proc = subprocess.run([str(ff_path), '--raw' if legacy else '--file-raw', proc = subprocess.run([str(ff_path), '--raw' if legacy else '--file-raw',
str(path.absolute()), *shlex.split(args)]) str(path.absolute()), *shlex.split(args)])
+171 -51
View File
@@ -300,6 +300,9 @@ PRESETS: dict[str, ColorProfile] = {
'#ABABAB', '#ABABAB',
'#000000' '#000000'
]), ]),
# https://orientation.fandom.com/wiki/Fictosexual
'fictosexual': ColorProfile(["#000000", "#C4C4C4", "#A349A5", "#C4C4C4", "#000000"]),
# aroace1 sourced from https://flag.library.lgbt/flags/aroace/ # aroace1 sourced from https://flag.library.lgbt/flags/aroace/
'aroace1': ColorProfile([ 'aroace1': ColorProfile([
@@ -550,6 +553,27 @@ PRESETS: dict[str, ColorProfile] = {
"3ecdf9", "3ecdf9",
"fff48d", "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([ "finsexual": ColorProfile([
"#B18EDF", "#B18EDF",
@@ -704,53 +728,56 @@ PRESETS: dict[str, ColorProfile] = {
# sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg # sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg
'bear': ColorProfile([ 'bear': ColorProfile([
'#623804', '#623804',
'#D56300', '#D56300',
'#FEDD63', '#FEDD63',
'#FEE6B8', '#FEE6B8',
'#FFFFFF', '#FFFFFF',
'#555555', '#555555',
]), ]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png # colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png
'butch': ColorProfile([ 'butch': ColorProfile([
'#D72800', '#D72800',
'#F17623', '#F17623',
'#FF9C56', '#FF9C56',
'#FFFDF6', '#FFFDF6',
'#FFCE89', '#FFCE89',
'#FEAF02', '#FEAF02',
'#A37000', '#A37000',
]), ]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg # colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg
'leather': ColorProfile([ 'leather': ColorProfile([
'#000000', '#000000',
'#252580', '#252580',
'#000000', '#000000',
'#252580', '#252580',
'#FFFFFF', '#FFFFFF',
'#252580', '#252580',
'#000000', '#000000',
'#252580', '#252580',
'#000000', '#000000',
]), ]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg # colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg
'otter': ColorProfile([ 'otter': ColorProfile([
'#263881', '#263881',
'#5C9DC9', '#5C9DC9',
'#FFFFFF', '#FFFFFF',
'#3A291D', '#3A291D',
'#5C9DC9', '#5C9DC9',
'#263881', '#263881',
]), ]),
# colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg # colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg
'twink': ColorProfile([ 'twink': ColorProfile([
'#FFB2FF', '#FFB2FF',
'#FFFFFF', '#FFFFFF',
'#FFFF81', '#FFFF81',
]), ]),
# https://en.wikipedia.org/wiki/File:FatFetishFlag.png
'adipophilia': ColorProfile(["#000000", "#E16180", "#FFF9BE", "#603E41", "#000000"]),
'kenochoric': ColorProfile([ 'kenochoric': ColorProfile([
'#000000', '#000000',
@@ -924,6 +951,35 @@ PRESETS: dict[str, ColorProfile] = {
"#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 # Meme flags
'beiyang': ColorProfile([ 'beiyang': ColorProfile([
'#DF1B12', '#DF1B12',
@@ -949,27 +1005,91 @@ PRESETS: dict[str, ColorProfile] = {
"#B71212" "#B71212"
]), ]),
# colors from Gilbert Baker's original 1978 flag design 'band': ColorProfile([
# used https://gilbertbaker.com/rainbow-flag-color-meanings/ as source and colorpicked "#2670c0",
'baker': ColorProfile([ "#f5bd00",
'#F23D9E', "#dc0045",
'#F80A24', "#e0608e"
'#F78022', ]),
'#F9E81F',
'#1E972E', # Adding libragender flags https://lgbtqia.wiki/wiki/Libragender
'#1B86BC', # Sourced from https://lgbtqia.wiki/wiki/Libragender
'#243897', 'libragender': ColorProfile([
'#6F0A82', "#000000",
"#808080",
"#92D8E9",
"#FFF544",
"#FFB0CA",
"#808080",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Librafeminine
'librafeminine': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#C6568F",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Libramasculine
'libramasculine': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#56C5C5",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Librandrogyne
'libraandrogyne': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#9186B1",
"#FFFFFF",
"#A3A3A3",
"#000000"
]),
# Sourced from https://lgbtqia.wiki/wiki/Libranonbinary
'libranonbinary': ColorProfile([
"#000000",
"#A3A3A3",
"#FFFFFF",
"#FFF987",
"#FFFFFF",
"#A3A3A3",
"#000000"
]), ]),
# this is 4 all the dogs, from zombpawcoins on tumblr! # Adding Fluidflux flags - ObsoleteDev
'caninekin': ColorProfile([ # Sourced from https://gender.fandom.com/wiki/Fluidflux?file=FC90B24D-CA36-4FE2-A752-C9ABFC65E332.jpeg
'#2d2822',
'#543d25', 'fluidflux A': ColorProfile([
'#9c754d', "#ff115f",
'#e8dac2', "#a34aa3",
'#cfad8c', "#00a4e7",
'#b77b55', "#ffdf00",
'#954e31' "#000000",
]) "#ffed71",
"#85daff",
"#dbadda",
"#fe8db1"
]),
'fluidflux B': ColorProfile([
"#c6d1d2",
"#f47b9d",
"#f09f9b",
"#e3f09e",
"#75eeea",
"#52d2ed",
"#c6d1d2"
]),
} }
+2 -2
View File
@@ -105,8 +105,8 @@ def start_animation():
try: try:
while 1: while 1:
# Clear the screen # Move cursor to the top left corner
print("\033[2J\033[H", end="") print("\033[H", end="")
draw_frame() draw_frame()
frame += speed frame += speed
sleep(frame_delay) sleep(frame_delay)
+4 -1
View File
@@ -1,3 +1,4 @@
import os
import platform import platform
import subprocess import subprocess
import sys import sys
@@ -12,7 +13,9 @@ def run_rust():
pd = Path(__file__).parent.joinpath('rust') pd = Path(__file__).parent.joinpath('rust')
pd = pd.joinpath('hyfetch.exe' if platform.system() == 'Windows' else 'hyfetch') pd = pd.joinpath('hyfetch.exe' if platform.system() == 'Windows' else 'hyfetch')
if not pd.exists(): if not pd.exists():
printc('&cThe rust executable is not found, falling back to python...') if 'HYFETCH_DONT_WARN_RUST' not in os.environ:
printc('&cThe executable for hyfetch v2 (rust) is not found, falling back to legacy v1.99.∞ (python).\n'
'You can add environment variable HYFETCH_DONT_WARN_RUST=1 to suppress this warning.\n')
run_py() run_py()
return return
+366 -295
View File
@@ -28,7 +28,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
version=7.98.0 version=8.0.2
# Fallback to a value of '5' for shells which support bash # Fallback to a value of '5' for shells which support bash
# but do not set the 'BASH_' shell variables (osh). # but do not set the 'BASH_' shell variables (osh).
@@ -225,7 +225,7 @@ package_managers="on"
# Show separate user and system packages for supported package managers # Show separate user and system packages for supported package managers
# #
# Default: 'on' # Default: 'on'
# Values: 'on', 'off' # Values: 'on', 'off'
# Flag: --package_separate # Flag: --package_separate
@@ -868,7 +868,7 @@ image_source="auto"
# NOTE: Adélie, aerOS, Afterglow, AIX, AlmaLinux, Alpine, Alter, Amazon, AmogOS, Anarchy, Android, # NOTE: Adélie, aerOS, Afterglow, AIX, AlmaLinux, Alpine, Alter, Amazon, AmogOS, Anarchy, Android,
# Antergos, antiX, AOSC OS, Aperio GNU/Linux, Aperture, Apricity, Arch, ArchBox, Archcraft, # Antergos, antiX, AOSC OS, Aperio GNU/Linux, Aperture, Apricity, Arch, ArchBox, Archcraft,
# archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike, ArcoLinux, Arkane, ArseLinux, # archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike, ArcoLinux, Arkane, ArseLinux,
# Artix, Arya, Asahi, AsteroidOS, astOS, Astra Linux, Athena, azos, Bedrock, BigLinux, # Artix, Arya, Asahi, AsteroidOS, astOS, Astra Linux, Athena, azos, Bazzite, Bedrock, BigLinux,
# BigLinux_large, Bitrig, BlackArch, BlackMesa, blackPanther, BLAG, BlankOn, BlueLight, Bodhi, # BigLinux_large, Bitrig, BlackArch, BlackMesa, blackPanther, BLAG, BlankOn, BlueLight, Bodhi,
# bonsai, BSD, BunsenLabs, CachyOS, Calculate, CalinixOS, Carbs, CBL-Mariner, CelOS, Center, CentOS, # bonsai, BSD, BunsenLabs, CachyOS, Calculate, CalinixOS, Carbs, CBL-Mariner, CelOS, Center, CentOS,
# Chakra, ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro, Clear Linux OS, ClearOS, # Chakra, ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro, Clear Linux OS, ClearOS,
@@ -877,28 +877,29 @@ image_source="auto"
# DracOS, DragonFly, Drauger, Droidian, Elementary, Elive, EncryptOS, EndeavourOS, Endless, Enso, # DracOS, DragonFly, Drauger, Droidian, Elementary, Elive, EncryptOS, EndeavourOS, Endless, Enso,
# EuroLinux, EvolutionOS, eweOS, Exherbo, Exodia Predator OS, Fedora, Fedora Kinoite, Fedora # EuroLinux, EvolutionOS, eweOS, Exherbo, Exodia Predator OS, Fedora, Fedora Kinoite, Fedora
# Sericea, Fedora Silverblue, Fedora_unicode, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT, # Sericea, Fedora Silverblue, Fedora_unicode, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT,
# Frugalware, Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus, gNewSense, GNOME, GNU, # Frugalware, Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus, Gloire, gNewSense,
# GoboLinux, GrapheneOS, Grombyang, Guix, Haiku, HamoniKR, HarDClanZ, Hash, Huayra, Hybrid, HydroOS, # GNOME, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, Haiku, HamoniKR, HarDClanZ, Hash, Huayra,
# Hyperbola, iglunix, instantOS, Interix, IRIX, Ironclad, Itc, januslinux, Kaisen, Kali, KaOS, KDE, # Hybrid, HydroOS, Hyperbola, iglunix, instantOS, Interix, IRIX, Itc, januslinux, Kaisen, Kali,
# Kibojoe, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LainOS, LangitKetujuh, LaxerOS, LEDE, # KaOS, KDE, Kibojoe, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LainOS, LangitKetujuh, LaxerOS,
# LibreELEC, Linspire, Linux, Linux Lite, Linux Mint, Linux Mint Old, LinuxFromScratch, Live Raizo, # LEDE, LibreELEC, Linspire, Linux, Linux Lite, Linux Mint, Linux Mint Old, LinuxFromScratch, Live
# LMDE, Lubuntu, Lunar, mac, MacaroniOS, Mageia, Magix, MagpieOS, MainsailOS, Mandriva, Manjaro, # Raizo, LMDE, Lubuntu, Lunar, mac, MacaroniOS, Mageia, Magix, MagpieOS, MainsailOS, Mandriva,
# MassOS, MatuusOS, Maui, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, NekOS, Neptune, # Manjaro, MassOS, MatuusOS, Maui, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, NekOS,
# NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful, Nobara, NomadBSD, Nurunner, NuTyX, Obarun, # Neptune, NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful, Nobara, NomadBSD, Nurunner, NuTyX,
# OBRevenge, OmniOS, Open Source Media Center, OpenBSD, openEuler, OpenIndiana, openKylin, # Obarun, OBRevenge, OmniOS, Open Source Media Center, OpenBSD, openEuler, OpenIndiana, openKylin,
# openmamba, OpenMandriva, OpenStage, openSUSE, openSUSE Leap, openSUSE Tumbleweed, OPNsense, # openmamba, OpenMandriva, OpenStage, openSUSE, openSUSE Leap, openSUSE Tumbleweed, openSUSE
# Oracle, orchid, OS Elbrus, PacBSD, Panwah, Parabola, parch, Pardus, Parrot, Parsix, PCBSD, # Tumbleweed-Slowroll, OPNsense, Oracle, orchid, OS Elbrus, PacBSD, Panwah, Parabola, parch, Pardus,
# PCLinuxOS, pearOS, Pengwin, Pentoo, Peppermint, Peropesis, phyOS, PikaOS, Pisi, PNM Linux, # Parrot, Parsix, PCBSD, PCLinuxOS, pearOS, Pengwin, Pentoo, Peppermint, Peropesis, phyOS, PikaOS,
# Pop!_OS, Porteus, PostMarketOS, Profelis SambaBOX, Proxmox, PuffOS, Puppy, PureOS, Q4OS, Qubes, # Pisi, PNM Linux, Pop!_OS, Porteus, PostMarketOS, Profelis SambaBOX, Proxmox, PuffOS, Puppy,
# Qubyt, Quibian, Radix, Raspbian, ravynOS, Reborn OS, Red Star, Redcore, Redhat, Refracted Devuan, # PureOS, Q4OS, Qubes, Qubyt, Quibian, Radix, Raspbian, ravynOS, Reborn OS, Red Star, Redcore,
# Regata, Regolith, RhaymOS, rocky, Rosa, Sabayon, sabotage, Sailfish, SalentOS, Salient OS, Salix, # Redhat, Refracted Devuan, Regata, Regolith, RhaymOS, Rhino Linux, rocky, Rosa, Sabayon, sabotage,
# Sasanqua, Scientific, semc, Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, # Sailfish, SalentOS, Salient OS, Salix, Sasanqua, Scientific, semc, Septor, Serene, SharkLinux,
# Slackware, SliTaz, SmartOS, Soda, Solus, Source Mage, Sparky, Star, SteamOS, Stock Linux, Sulin, # ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SliTaz, SmartOS, Soda, Solus, Source Mage,
# SunOS, SwagArch, t2, Tails, Tatra, TeArch, TorizonCore, Trisquel, Twister, Ubuntu, Ubuntu Budgie, # Sparky, Star, SteamOS, Stock Linux, Sulin, SunOS, SwagArch, t2, Tails, Tatra, TeArch, TorizonCore,
# Ubuntu Cinnamon, Ubuntu Kylin, Ubuntu MATE, Ubuntu Studio, Ubuntu Sway, Ubuntu Touch, Ubuntu- # Trisquel, Twister, Ubuntu, Ubuntu Budgie, Ubuntu Cinnamon, Ubuntu Kylin, Ubuntu MATE, Ubuntu
# GNOME, ubuntu_old02, Ultramarine Linux, unicodearch, Univalent, Univention, Uos, UrukOS, uwuntu, # Studio, Ubuntu Sway, Ubuntu Touch, Ubuntu-GNOME, ubuntu_old02, Ultramarine Linux, unicodearch,
# Vanilla, Venom, VNux, Void, VzLinux, wii-linux-ngx, Windows, Windows 10, Windows 11, Windows95, # Univalent, Univention, Uos, UrukOS, uwuntu, Vanilla, Venom, VNux, Void, VzLinux, wii-linux-ngx,
# Wrt, Xenia, Xenia2, XFerience, Xray_OS, Xubuntu, yiffOS, Zorin have ascii logos. # Windows, Windows 10, Windows 11, Windows95, Wrt, Xenia, Xenia2, XFerience, Xray_OS, Xubuntu,
# yiffOS, Zorin have ascii logos.
# NOTE: arch, dragonfly, Fedora, LangitKetujuh, nixos, redhat, Ubuntu have 'old' logo variants, use # NOTE: arch, dragonfly, Fedora, LangitKetujuh, nixos, redhat, Ubuntu have 'old' logo variants, use
# {distro}_old to use them. # {distro}_old to use them.
@@ -1070,8 +1071,7 @@ get_distro() {
[[ $distro ]] && return [[ $distro ]] && return
case $os in case $os in
Ironclad) distro=Ironclad ;; Linux|BSD|MINIX|Ironclad)
Linux|BSD|MINIX)
if [[ -f /bedrock/etc/bedrock-release && -z $BEDROCK_RESTRICT ]]; then if [[ -f /bedrock/etc/bedrock-release && -z $BEDROCK_RESTRICT ]]; then
case $distro_shorthand in case $distro_shorthand in
on|tiny) distro="Bedrock Linux" ;; on|tiny) distro="Bedrock Linux" ;;
@@ -1145,7 +1145,7 @@ get_distro() {
on|tiny) distro="LindowsOS" ;; on|tiny) distro="LindowsOS" ;;
*) distro="$(awk '/Version/ {print $2,$3}' /etc/lindowsos-version)" *) distro="$(awk '/Version/ {print $2,$3}' /etc/lindowsos-version)"
esac esac
elif [[ -f /etc/astra_version ]]; then elif [[ -f /etc/astra_version ]]; then
distro="Astra Linux" distro="Astra Linux"
distro_version="$(sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' < /etc/astra_version)" distro_version="$(sed -nr 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' < /etc/astra_version)"
@@ -1199,7 +1199,15 @@ get_distro() {
elif type -p lsb_release >/dev/null; then elif type -p lsb_release >/dev/null; then
# Debian does not include .x versions in /etc/os-version, but does in debian_version # Debian does not include .x versions in /etc/os-version, but does in debian_version
# So if that file exists, and we are not *buntu, build name from there # So if that file exists, and we are not *buntu, build name from there
if [[ -f /etc/debian_version ]] && [[ $(lsb_release -si) != *"buntu"* ]] && [[ $(lsb_release -si) != *"neon"* ]]; then if [[ $(lsb_release -si) = *"KSLinux"* ]]
then
. /etc/os-release
case $distro_shorthand in
on) distro=${NAME} ;;
tiny) distro=${NAME} ;;
*) distro="${NAME} ${VERSION}" ;;
esac
elif [[ -f /etc/debian_version ]] && [[ $(lsb_release -si) != *"buntu"* ]] && [[ $(lsb_release -si) != *"neon"* ]]; then
. /etc/os-release . /etc/os-release
case $distro_shorthand in case $distro_shorthand in
on) distro="${NAME}" ;; on) distro="${NAME}" ;;
@@ -1258,10 +1266,6 @@ get_distro() {
elif type -p tazpkg >/dev/null; then elif type -p tazpkg >/dev/null; then
distro="SliTaz $(< /etc/slitaz-release)" distro="SliTaz $(< /etc/slitaz-release)"
elif type -p kpt >/dev/null && \
type -p kpm >/dev/null; then
distro=KSLinux
elif [[ -d /system/app/ && -d /system/priv-app ]]; then elif [[ -d /system/app/ && -d /system/priv-app ]]; then
distro="Android $(getprop ro.build.version.release)" distro="Android $(getprop ro.build.version.release)"
@@ -1345,7 +1349,7 @@ get_distro() {
windows_version_verbose=$(trim "${windows_version_verbose/REG_SZ}") windows_version_verbose=$(trim "${windows_version_verbose/REG_SZ}")
buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_) buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_)
windows_version_verbose=$(trim "${windows_version_verbose/Windows}") windows_version_verbose=$(trim "${windows_version_verbose/Windows}")
windows_buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_) windows_buildnumber=$(reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -v CurrentBuildNumber | grep REG_)
windows_buildnumber=${buildnumber/CurrentBuildNumber} windows_buildnumber=${buildnumber/CurrentBuildNumber}
windows_buildnumber=${buildnumber/REG_SZ} windows_buildnumber=${buildnumber/REG_SZ}
if [[ "$windows_version_verbose" == *"10"* ]] && (( windows_buildnumber >= 22000 )); then if [[ "$windows_version_verbose" == *"10"* ]] && (( windows_buildnumber >= 22000 )); then
@@ -1819,6 +1823,10 @@ get_model() {
iPod5,1): "iPod touch 5G" ;; iPod5,1): "iPod touch 5G" ;;
iPod7,1): "iPod touch 6G" ;; iPod7,1): "iPod touch 6G" ;;
iPod9,1): "iPod touch 7G" ;; iPod9,1): "iPod touch 7G" ;;
AppleTV2,1): "Apple TV 2" ;;
AppleTV3,1): "Apple TV 3" ;;
AppleTV3,2): "Apple TV 3 (2013)" ;;
esac esac
model=$_ model=$_
@@ -1932,7 +1940,7 @@ get_kernel() {
*) kernel=${ver} ;; *) kernel=${ver} ;;
esac esac
return return
} }
# In Interix the Kernel version is commonly comprised of the host OS build number, # In Interix the Kernel version is commonly comprised of the host OS build number,
# OS architecture and Interix build number # OS architecture and Interix build number
@@ -1957,9 +1965,6 @@ get_kernel() {
get_uptime() { get_uptime() {
# Get uptime in seconds. # Get uptime in seconds.
case $os in case $os in
Ironclad)
s=$(uptime -s)
;;
Linux|Windows|MINIX) Linux|Windows|MINIX)
if [[ -r /proc/uptime ]]; then if [[ -r /proc/uptime ]]; then
s=$(< /proc/uptime) s=$(< /proc/uptime)
@@ -1988,7 +1993,7 @@ get_uptime() {
s=$((now - boot)) s=$((now - boot))
;; ;;
AIX|IRIX) AIX|IRIX|Ironclad)
t=$(LC_ALL=POSIX ps -o etime= -p 1) t=$(LC_ALL=POSIX ps -o etime= -p 1)
[[ $t == *-* ]] && { d=${t%%-*}; t=${t#*-}; } [[ $t == *-* ]] && { d=${t%%-*}; t=${t#*-}; }
@@ -2183,7 +2188,7 @@ get_packages() {
has evox && tot cat /var/evox/packages/DB has evox && tot cat /var/evox/packages/DB
has squirrel && tot ls -w 1 /var/packages has squirrel && tot ls -w 1 /var/packages
has anise && tot anise s --installed has anise && tot anise s --installed
has am && pac "$(am -f --less)" has am && pac "$(am -f --less | awk '{ SUM += $1} END { print SUM }')"
# Bonsai refers to the now archived repo https://github.com/mitchweaver/bonsai # Bonsai refers to the now archived repo https://github.com/mitchweaver/bonsai
has bonsai && tot bonsai list has bonsai && tot bonsai list
# Rad refers to https://github.com/glaucuslinux/rad # Rad refers to https://github.com/glaucuslinux/rad
@@ -2192,7 +2197,7 @@ get_packages() {
has radula && tot radula -cl has radula && tot radula -cl
# https://github.com/birb-linux/birb # https://github.com/birb-linux/birb
has birb && tot birb --list-installed has birb && tot birb --list-installed
has port && pkgs_h=1 tot port installed && ((packages-=1)) has port && pkgs_h=1 tot port installed && ((packages-=1))
# Using the dnf package cache is much faster than rpm. # Using the dnf package cache is much faster than rpm.
@@ -2241,7 +2246,6 @@ get_packages() {
fi fi
# Other (Needs complex command) # Other (Needs complex command)
has kpm-pkg && ((packages+=$(kpm --get-selections | grep -cv deinstall$)))
nix-user-pkgs() { nix-user-pkgs() {
if [ -d ~/.nix-profile ]; then if [ -d ~/.nix-profile ]; then
@@ -2324,7 +2328,7 @@ get_packages() {
has snap && ps -e | grep -qFm 1 snapd >/dev/null && \ has snap && ps -e | grep -qFm 1 snapd >/dev/null && \
pkgs_h=1 tot snap list && ((packages-=1)) pkgs_h=1 tot snap list && ((packages-=1))
# Get AppImageLauncher Application directory, # Get AppImageLauncher Application directory,
# See: https://github.com/TheAssassin/AppImageLauncher/wiki/Configuration#settings-file # See: https://github.com/TheAssassin/AppImageLauncher/wiki/Configuration#settings-file
manager=appimage && has appimaged || [ -f "$HOME"/.config/appimagelauncher.cfg ] && manager=appimage && has appimaged || [ -f "$HOME"/.config/appimagelauncher.cfg ] &&
if [ -f "$HOME"/.config/appimagelauncher.cfg ]; then if [ -f "$HOME"/.config/appimagelauncher.cfg ]; then
@@ -2336,7 +2340,7 @@ get_packages() {
AIDIR="$HOME/.local/bin" AIDIR="$HOME/.local/bin"
fi && fi &&
dir "$AIDIR/*.[Aa]pp[Ii]mage \ dir "$AIDIR/*.[Aa]pp[Ii]mage \
$ALDIR/*.[Aa]pp[Ii]mage" $ALDIR/*.[Aa]pp[Ii]mage"
# Has devbox & is initialized # Has devbox & is initialized
has devbox && [[ "$(devbox global list)" != *"not activated"* ]] && tot devbox global list has devbox && [[ "$(devbox global list)" != *"not activated"* ]] && tot devbox global list
@@ -2519,7 +2523,7 @@ get_editor() {
esac esac
editor_v="${editor_v/$'\n'*}" editor_v="${editor_v/$'\n'*}"
editor_v="${editor_v/Version: }" editor_v="${editor_v/Version: }"
# Only show editor name if the version string doesn't contain it # Only show editor name if the version string doesn't contain it
echo "$editor_v" | grep -i "$editor_name" &> /dev/null && editor_name="" echo "$editor_v" | grep -i "$editor_name" &> /dev/null && editor_name=""
editor=${editor_name}${editor_v:+ }${editor_v} editor=${editor_name}${editor_v:+ }${editor_v}
@@ -2983,7 +2987,7 @@ get_wm_theme() {
wm_theme=$(leftwm-theme status | grep "Your current theme" | sed -e 's/Your current theme is //g' -e 's/\,.*$//g') wm_theme=$(leftwm-theme status | grep "Your current theme" | sed -e 's/Your current theme is //g' -e 's/\,.*$//g')
fi fi
;; ;;
fly-wm) fly-wm)
fly_config_file fly_config_file
if grep -q 'DecorTheme' "${fly_config_file}" 2>/dev/null; then if grep -q 'DecorTheme' "${fly_config_file}" 2>/dev/null; then
@@ -3175,19 +3179,10 @@ END
;; ;;
"Mac OS X"|"macOS") "Mac OS X"|"macOS")
if [[ $osx_version == 10.[4-5]* ]]; then cpu="$(sysctl -n machdep.cpu.brand_string 2>/dev/null)"
cpu="$(system_profiler SPHardwareDataType | grep CPU\ Type)" if [ -z "$cpu" ]; then
cpu="${cpu/CPU\ Type\:/}" cpu="$(system_profiler SPHardwareDataType | grep Processor\ Name)"
cpu="${cpu/Processor\ Name\:/}"
speed="$(system_profiler SPHardwareDataType | grep CPU\ Speed)"
speed="${speed/CPU\ Speed\:/}"
speed="${speed/ MHz/}"
speed="${speed/ GHz/}"
cores="$(system_profiler SPHardwareDataType | grep Number\ Of\ CPUs)"
cores="${cores/Number\ Of\ CPUs\:/}"
else
cpu="$(sysctl -n machdep.cpu.brand_string)"
fi fi
# Get CPU cores. # Get CPU cores.
@@ -3241,6 +3236,10 @@ END
"iPad7,"[1-4]): "Apple A10X Fusion (6) @ 2.39GHz" ;; "iPad7,"[1-4]): "Apple A10X Fusion (6) @ 2.39GHz" ;;
"iPad8,"[1-8]): "Apple A12X Bionic (8) @ 2.49GHz" ;; "iPad8,"[1-8]): "Apple A12X Bionic (8) @ 2.49GHz" ;;
"iPad8,9" | "iPad8,1"[0-2]): "Apple A12Z 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 esac
cpu="$_" cpu="$_"
;; ;;
@@ -3374,7 +3373,6 @@ END
cpu="${cpu//[1-9][0-9]-Core}" cpu="${cpu//[1-9][0-9]-Core}"
cpu="${cpu//[0-9]-Core}" cpu="${cpu//[0-9]-Core}"
cpu="${cpu//, * Compute Cores}" cpu="${cpu//, * Compute Cores}"
cpu="${cpu//Core / }"
cpu="${cpu//(\"AuthenticAMD\"*)}" cpu="${cpu//(\"AuthenticAMD\"*)}"
cpu="${cpu//with Radeon*Graphics}" cpu="${cpu//with Radeon*Graphics}"
cpu="${cpu//, altivec supported}" cpu="${cpu//, altivec supported}"
@@ -3547,10 +3545,12 @@ get_gpu() {
"iPhone OS") "iPhone OS")
case $kernel_machine in case $kernel_machine in
"iPhone1,"[1-2]): "PowerVR MBX Lite 3D" ;; "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" "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" ;; "iPhone5,"[1-4]): "PowerVR SGX543MP3" ;;
"iPhone6,"[1-2] | "iPad4,"[1-9]): "PowerVR G6430" ;; "iPhone6,"[1-2] | "iPad4,"[1-9]): "PowerVR G6430" ;;
"iPhone7,"[1-2] | "iPod7,1" | "iPad5,"[1-2]): "PowerVR GX6450" ;; "iPhone7,"[1-2] | "iPod7,1" | "iPad5,"[1-2]): "PowerVR GX6450" ;;
@@ -3663,13 +3663,19 @@ get_memory() {
;; ;;
"Mac OS X" | "macOS" | "iPhone OS") "Mac OS X" | "macOS" | "iPhone OS")
hw_pagesize="$(sysctl -n hw.pagesize)" pageable="$(sysctl -n vm.page_pageable_internal_count)"
mem_total="$(($(sysctl -n hw.memsize) / 1024))" purgeable="$(sysctl -n vm.page_purgeable_count)"
pages_app="$(($(sysctl -n vm.page_pageable_internal_count) - $(sysctl -n vm.page_purgeable_count)))" if [ -n "$pageable" ] && [ -n "$purgeable" ] && type -p vm_stat &>/dev/null; then
pages_wired="$(vm_stat | awk '/ wired/ { print $4 }')" hw_pagesize="$(sysctl -n hw.pagesize)"
pages_compressed="$(vm_stat | awk '/ occupied/ { printf $5 }')" mem_total="$(($(sysctl -n hw.memsize) / 1024))"
pages_compressed="${pages_compressed:-0}" pages_app="$(($(sysctl -n vm.page_pageable_internal_count) - $(sysctl -n vm.page_purgeable_count)))"
mem_used="$(((pages_app + ${pages_wired//.} + ${pages_compressed//.}) * hw_pagesize / 1024))" pages_wired="$(vm_stat | awk '/ wired/ { print $4 }')"
pages_compressed="$(vm_stat | awk '/ occupied/ { printf $5 }')"
pages_compressed="${pages_compressed:-0}"
mem_used="$(((pages_app + ${pages_wired//.} + ${pages_compressed//.}) * hw_pagesize / 1024))"
else
return 0
fi
;; ;;
"BSD" | "MINIX" | "ravynOS") "BSD" | "MINIX" | "ravynOS")
@@ -4171,6 +4177,7 @@ get_resolution() {
"iPad13,"[1-2] | "iPad13,1"[6-9]): "1640x2360" ;; "iPad13,"[1-2] | "iPad13,1"[6-9]): "1640x2360" ;;
"iPad8,"[1-4] | "iPad8,"[9-10] | "iPad13,"[4-7] | "iPad14,"[3-6]): "1668x2388" ;; "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" ;; "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 esac
resolution="$_" resolution="$_"
;; ;;
@@ -4587,7 +4594,7 @@ get_python_ver() {
if command -v python &> /dev/null; then if command -v python &> /dev/null; then
python_ver=$(python -VVV) python_ver=$(python -VVV)
python_ver=${python_ver//$'\n'/} python_ver=${python_ver//$'\n'/}
python_ver=${python_ver//\(+([^\)])\)} python_ver=${python_ver//+([[:space:]])\(+([^\)])\)}
python_ver=$(echo "$python_ver" | awk '$1=$1') python_ver=$(echo "$python_ver" | awk '$1=$1')
fi fi
} }
@@ -5305,6 +5312,16 @@ get_local_ip() {
} }
get_public_ip() { get_public_ip() {
if [[ -z "$public_ip_host" ]] && type -p dig >/dev/null; then
public_ip="$(dig +time=1 +tries=1 +short TXT ch whoami.cloudflare @1.1.1.1 | tr -d '"')"
[[ "$public_ip" =~ ^\; ]] && unset public_ip
fi
if [[ -z "$public_ip_host" ]] && [[ -z "$public_ip" ]] && type -p drill >/dev/null; then
public_ip="$(drill whoami.cloudflare TXT CH @1.1.1.1 | awk '/^whoami\./ && $3 == "CH" {print $5}' | tr -d '"')"
fi
# Fallback
if [[ -z "$public_ip_host" ]] && type -p dig >/dev/null; then if [[ -z "$public_ip_host" ]] && type -p dig >/dev/null; then
public_ip="$(dig +time=1 +tries=1 +short myip.opendns.com @resolver1.opendns.com)" public_ip="$(dig +time=1 +tries=1 +short myip.opendns.com @resolver1.opendns.com)"
[[ "$public_ip" =~ ^\; ]] && unset public_ip [[ "$public_ip" =~ ^\; ]] && unset public_ip
@@ -6620,12 +6637,12 @@ ASCII:
GNU/Linux, Aperture, Apricity, Arch, ArchBox, Archcraft, GNU/Linux, Aperture, Apricity, Arch, ArchBox, Archcraft,
archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike, archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike,
ArcoLinux, Arkane, ArseLinux, Artix, Arya, Asahi, AsteroidOS, astOS, ArcoLinux, Arkane, ArseLinux, Artix, Arya, Asahi, AsteroidOS, astOS,
Astra Linux, Athena, azos, Bedrock, BigLinux, BigLinux_large, Astra Linux, Athena, azos, Bazzite, Bedrock, BigLinux,
Bitrig, BlackArch, BlackMesa, blackPanther, BLAG, BlankOn, BigLinux_large, Bitrig, BlackArch, BlackMesa, blackPanther, BLAG,
BlueLight, Bodhi, bonsai, BSD, BunsenLabs, CachyOS, Calculate, BlankOn, BlueLight, Bodhi, bonsai, BSD, BunsenLabs, CachyOS,
CalinixOS, Carbs, CBL-Mariner, CelOS, Center, CentOS, Chakra, Calculate, CalinixOS, Carbs, CBL-Mariner, CelOS, Center, CentOS,
ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro, Clear Chakra, ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro,
Linux OS, ClearOS, Clover, Cobalt, Condres, Container Linux by Clear Linux OS, ClearOS, Clover, Cobalt, Condres, Container Linux by
CoreOS, CRUX, Crystal Linux, Cucumber, CutefishOS, CuteOS, CyberOS, CoreOS, CRUX, Crystal Linux, Cucumber, CutefishOS, CuteOS, CyberOS,
dahlia, DarkOs, Darwin, Debian, Deepin, DesaOS, Devuan, DietPi, dahlia, DarkOs, Darwin, Debian, Deepin, DesaOS, Devuan, DietPi,
digital UNIX, DracOS, DragonFly, Drauger, Droidian, Elementary, digital UNIX, DracOS, DragonFly, Drauger, Droidian, Elementary,
@@ -6634,37 +6651,37 @@ ASCII:
Kinoite, Fedora Sericea, Fedora Silverblue, Fedora_unicode, Kinoite, Fedora Sericea, Fedora Silverblue, Fedora_unicode,
FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT, Frugalware, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT, Frugalware,
Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus, Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus,
gNewSense, GNOME, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, Gloire, gNewSense, GNOME, GNU, GoboLinux, GrapheneOS, Grombyang,
Haiku, HamoniKR, HarDClanZ, Hash, Huayra, Hybrid, HydroOS, Guix, Haiku, HamoniKR, HarDClanZ, Hash, Huayra, Hybrid, HydroOS,
Hyperbola, iglunix, instantOS, Interix, IRIX, Ironclad, Itc, Hyperbola, iglunix, instantOS, Interix, IRIX, Itc, januslinux,
januslinux, Kaisen, Kali, KaOS, KDE, Kibojoe, Kogaion, Korora, Kaisen, Kali, KaOS, KDE, Kibojoe, Kogaion, Korora, KrassOS, KSLinux,
KrassOS, KSLinux, Kubuntu, LainOS, LangitKetujuh, LaxerOS, LEDE, Kubuntu, LainOS, LangitKetujuh, LaxerOS, LEDE, LibreELEC, Linspire,
LibreELEC, Linspire, Linux, Linux Lite, Linux Mint, Linux Mint Old, Linux, Linux Lite, Linux Mint, Linux Mint Old, LinuxFromScratch,
LinuxFromScratch, Live Raizo, LMDE, Lubuntu, Lunar, mac, MacaroniOS, Live Raizo, LMDE, Lubuntu, Lunar, mac, MacaroniOS, Mageia, Magix,
Mageia, Magix, MagpieOS, MainsailOS, Mandriva, Manjaro, MassOS, MagpieOS, MainsailOS, Mandriva, Manjaro, MassOS, MatuusOS, Maui,
MatuusOS, Maui, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, NekOS, Neptune,
NekOS, Neptune, NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful, NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful, Nobara, NomadBSD,
Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, Open Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, Open Source Media
Source Media Center, OpenBSD, openEuler, OpenIndiana, openKylin, Center, OpenBSD, openEuler, OpenIndiana, openKylin, openmamba,
openmamba, OpenMandriva, OpenStage, openSUSE, openSUSE Leap, OpenMandriva, OpenStage, openSUSE, openSUSE Leap, openSUSE
openSUSE Tumbleweed, OPNsense, Oracle, orchid, OS Elbrus, PacBSD, Tumbleweed, openSUSE Tumbleweed-Slowroll, OPNsense, Oracle, orchid,
Panwah, Parabola, parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, OS Elbrus, PacBSD, Panwah, Parabola, parch, Pardus, Parrot, Parsix,
pearOS, Pengwin, Pentoo, Peppermint, Peropesis, phyOS, PikaOS, Pisi, PCBSD, PCLinuxOS, pearOS, Pengwin, Pentoo, Peppermint, Peropesis,
PNM Linux, Pop!_OS, Porteus, PostMarketOS, Profelis SambaBOX, phyOS, PikaOS, Pisi, PNM Linux, Pop!_OS, Porteus, PostMarketOS,
Proxmox, PuffOS, Puppy, PureOS, Q4OS, Qubes, Qubyt, Quibian, Radix, Profelis SambaBOX, Proxmox, PuffOS, Puppy, PureOS, Q4OS, Qubes,
Raspbian, ravynOS, Reborn OS, Red Star, Redcore, Redhat, Refracted Qubyt, Quibian, Radix, Raspbian, ravynOS, Reborn OS, Red Star,
Devuan, Regata, Regolith, RhaymOS, rocky, Rosa, Sabayon, sabotage, Redcore, Redhat, Refracted Devuan, Regata, Regolith, RhaymOS, Rhino
Sailfish, SalentOS, Salient OS, Salix, Sasanqua, Scientific, semc, Linux, rocky, Rosa, Sabayon, sabotage, Sailfish, SalentOS, Salient
Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, OS, Salix, Sasanqua, Scientific, semc, Septor, Serene, SharkLinux,
Slackware, SliTaz, SmartOS, Soda, Solus, Source Mage, Sparky, Star, ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SliTaz, SmartOS,
SteamOS, Stock Linux, Sulin, SunOS, SwagArch, t2, Tails, Tatra, Soda, Solus, Source Mage, Sparky, Star, SteamOS, Stock Linux, Sulin,
TeArch, TorizonCore, Trisquel, Twister, Ubuntu, Ubuntu Budgie, SunOS, SwagArch, t2, Tails, Tatra, TeArch, TorizonCore, Trisquel,
Ubuntu Cinnamon, Ubuntu Kylin, Ubuntu MATE, Ubuntu Studio, Ubuntu Twister, Ubuntu, Ubuntu Budgie, Ubuntu Cinnamon, Ubuntu Kylin,
Sway, Ubuntu Touch, Ubuntu-GNOME, ubuntu_old02, Ultramarine Linux, Ubuntu MATE, Ubuntu Studio, Ubuntu Sway, Ubuntu Touch, Ubuntu-GNOME,
unicodearch, Univalent, Univention, Uos, UrukOS, uwuntu, Vanilla, ubuntu_old02, Ultramarine Linux, unicodearch, Univalent, Univention,
Venom, VNux, Void, VzLinux, wii-linux-ngx, Windows, Windows 10, Uos, UrukOS, uwuntu, Vanilla, Venom, VNux, Void, VzLinux, wii-linux-
Windows 11, Windows95, Wrt, Xenia, Xenia2, XFerience, Xray_OS, ngx, Windows, Windows 10, Windows 11, Windows95, Wrt, Xenia, Xenia2,
Xubuntu, yiffOS, Zorin have ascii logos. XFerience, Xray_OS, Xubuntu, yiffOS, Zorin have ascii logos.
NOTE: arch, dragonfly, Fedora, LangitKetujuh, nixos, redhat, Ubuntu NOTE: arch, dragonfly, Fedora, LangitKetujuh, nixos, redhat, Ubuntu
have 'old' logo variants, use {distro}_old to use them. have 'old' logo variants, use {distro}_old to use them.
@@ -7434,7 +7451,7 @@ EOF
;; ;;
"ArseLinux"*) "ArseLinux"*)
set_colors 4 7 set_colors 4 7
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
@@ -7966,7 +7983,7 @@ EOF
"aerOS"*) "aerOS"*)
set_colors fg 0 0 0 set_colors fg 0 0 0
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
ooo OOO OOO ooo ooo OOO OOO ooo
oOO OOo oOO OOo
oOO OOo oOO OOo
@@ -8186,23 +8203,23 @@ EOF
"azos"*) "azos"*)
set_colors 6 1 set_colors 6 1
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ////. ${c2} ((((( ${c1} ////. ${c2} (((((
${c1} //////// ${c2} @(((((((( ${c1} //////// ${c2} @((((((((
${c1} //////// ${c2} @(((((((( ${c1} //////// ${c2} @((((((((
${c1} //////// /////// ${c2} ((((((( @(((((((( ${c1} //////// /////// ${c2} ((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @(((((((( ${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @(((((((( ${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ////// ${c2} (((((( ((((((((( @(((((((( ${c1} //////// ///////// ////// ${c2} (((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @(((((((( ${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @(((((((( ${c1} //////// ///////// //////// ${c2} (((((((( ((((((((( @((((((((
${c1} //////// ///////// //////// ${c2} ((((((( ((((((((( @(((((((( ${c1} //////// ///////// //////// ${c2} ((((((( ((((((((( @((((((((
${c1} //////// ///////// /// ${c2} ( ((((((((( @(((((((( ${c1} //////// ///////// /// ${c2} ( ((((((((( @((((((((
${c1} //////// ///////// ${c2} ((((((((( @(((((((( ${c1} //////// ///////// ${c2} ((((((((( @((((((((
${c1} //////// ///////// ${c2} &(((((((( @(((((((( ${c1} //////// ///////// ${c2} &(((((((( @((((((((
${c1} //////// ////// ${c2} @(((( @(((((((( ${c1} //////// ////// ${c2} @(((( @((((((((
${c1} //////// ${c2} @(((((((( ${c1} //////// ${c2} @((((((((
${c1} //////// ${c2} @(((((((( ${c1} //////// ${c2} @((((((((
${c1} ///// ${c2} ((((( ${c1} ///// ${c2} (((((
EOF EOF
;; ;;
@@ -8229,6 +8246,31 @@ ${c1}--------------------------------------
EOF EOF
;; ;;
"Bazzite"*)
set_colors 5 5
read -rd '' ascii_data <<'EOF'
${c1} %%%%%%====%%%%%%%%%%
%%%%%%%% %%%%%%%%%%%%%%
%%%%%%%%% %%%%%%%%%%%%%%%%
%%%%%%%%% %%%%%%%%%%%%%%%###
%%%%%%%%% %%%%%%%%%%%%%######
== =======######
== =========#####
%%%%%%%%% %%%%%%%####======#####
%%%%%%%%% %%%%%#######=====#####
%%%%%%%%% %%%#########=====#####
%%%%%%%%% %%##########=====#####
%%%%%%%%%====###########=====######
%%%%%%%%====#########======######
%%%%%%%=====#####========######
%%%%###===============#######
%#######==========#########
#######################
###################
###########
EOF
;;
"BigLinux_large"*) "BigLinux_large"*)
set_colors 2 3 4 7 set_colors 2 3 4 7
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
@@ -8336,7 +8378,7 @@ EOF
"BlackMesa") "BlackMesa")
set_colors 1 set_colors 1
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
.-;+$XHHHHHHX$+;-. .-;+$XHHHHHHX$+;-.
,;X@@X%/;=----=:/%X@@X/, ,;X@@X%/;=----=:/%X@@X/,
=$@@%=. .=+H@X: =$@@%=. .=+H@X:
@@ -8746,6 +8788,8 @@ ${c1} .
EOF EOF
;; ;;
"CelOS"*) "CelOS"*)
set_colors 4 6 0 5 set_colors 4 6 0 5
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
@@ -9252,16 +9296,16 @@ ${c2}
${c3}MMM1ua${c2} ${c3}MMM1ua${c2}
${c1}MM${c2}EE ${c3} MMMMM1uazE${c2} ${c1}MM${c2}EE ${c3} MMMMM1uazE${c2}
${c1}MM ${c2}EEEE ${c3}M1MM1uazzEn ${c2}EEEE MME ${c1}MM ${c2}EEEE ${c3}M1MM1uazzEn ${c2}EEEE MME
EEEEE ${c3}MMM uazEno ${c2}EEEE EEEEE ${c3}MMM uazEno ${c2}EEEE
EEEEE${c1}MMMMMMEno~; ${c2}EE E${c2} EEEEE${c1}MMMMMMEno~; ${c2}EE E${c2}
EE ${c1}MMMMMMMM~;;E ${c2}MMMMM M ${c2} EE ${c1}MMMMMMMM~;;E ${c2}MMMMM M ${c2}
E ${c1}MMMMMMMMM ${c2} E E ${c2} E ${c1}MMMMMMMMM ${c2} E E ${c2}
${c1}MMMMMMMMMMM ${c1}MMMMMMMMMMM
${c1}MMMMMMMMM ${c2}EE ${c1} ${c1}MMMMMMMMM ${c2}EE ${c1}
MM1MMMM ${c2}EEE ${c1} MM1MMMM ${c2}EEE ${c1}
MMMMM MMMMM
MMM MMM
M M
EOF EOF
;; ;;
@@ -9409,24 +9453,24 @@ EOF
"openKylin"*) "openKylin"*)
set_colors 2 7 set_colors 2 7
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
/KKK] /KKK]
KKKKKKK` ]KKKK\ KKKKKKK` ]KKKK\
KKKKK/ /KKKKKKKKK\ KKKKK/ /KKKKKKKKK\
KKKK/ ,KKKKKKKKKKKK^ KKKK/ ,KKKKKKKKKKKK^
,]KKK =KKK` /KKKKKKOOOOOO` ,]KKK =KKK` /KKKKKKOOOOOO`
,KKKKKK =KK /` [\OOOOOOO\ ,KKKKKK =KK /` [\OOOOOOO\
\KKKKK =K ,OOOOOOO` \KKKKK =K ,OOOOOOO`
,KKKKK =^ \OOOOOO ,KKKKK =^ \OOOOOO
,KKKK ^ OOOOOO^ ,KKKK ^ OOOOOO^
*KKK^ =OOOOO^ *KKK^ =OOOOO^
OOKK^ OOOOOO^ OOKK^ OOOOOO^
\OOOK\ /OOOOOO` \OOOK\ /OOOOOO`
OOOOOO] ,OOOOOOO^ OOOOOO] ,OOOOOOO^
,OOOOOOOO\] ,[OOOOOOOOO/ ,OOOOOOOO\] ,[OOOOOOOOO/
\OOOOOOOOOOOOOOOOOOOOO` \OOOOOOOOOOOOOOOOOOOOO`
[OOOOOOOOOOOOOOOO/` [OOOOOOOOOOOOOOOO/`
,[OOOOOOOOO] ,[OOOOOOOOO]
EOF EOF
;; ;;
@@ -9857,21 +9901,21 @@ EOF
"eweOS"*) "eweOS"*)
set_colors 7 11 9 8 1 set_colors 7 11 9 8 1
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c2} #####%%% ${c2} #####%%%
${c2} ##%%${c3}////${c2}%%%%%${c3}/// ${c2} ##%%${c3}////${c2}%%%%%${c3}///
${c2} #%%%%${c3}////((((////${c2}% ${c2} #%%%%${c3}////((((////${c2}%
${c1} *@@@@@@@${c3}/${c5},,,${c3}/////${c5},,,${c2}%${c1}@@@@@@@ ${c1} *@@@@@@@${c3}/${c5},,,${c3}/////${c5},,,${c2}%${c1}@@@@@@@
${c1} .@@@@@@@@@@@${c3}////////${c2}%%%${c1}@@@@@@@@@@@@ ${c1} .@@@@@@@@@@@${c3}////////${c2}%%%${c1}@@@@@@@@@@@@
${c1} @@@${c4}...${c1}@@@@@@${c3}////${c2}%%${c3}////${c1}@@@@@@@@@@@@@@@@ ${c1} @@@${c4}...${c1}@@@@@@${c3}////${c2}%%${c3}////${c1}@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@@@@@@@@@@@@@@@@@@ ${c1} @@@@@@@@@@@@@@@@@@@@@@@
${c1} @@@@@@ @@@@@@ ${c1} @@@@@@ @@@@@@
${c1} @@@ @@@ ${c1} @@@ @@@
EOF EOF
;; ;;
@@ -10769,7 +10813,7 @@ ${c2}~!!7JY5PGGBBBBBBBBGGGGGGGBGGGGGP5YJ?7~~~
EOF EOF
;; ;;
"Ironclad"*) "Gloire"*)
set_colors 5 7 0 set_colors 5 7 0
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c3} ${c3}
@@ -12093,7 +12137,7 @@ EOF
"nixos_small") "nixos_small")
set_colors 4 6 set_colors 4 6
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} \\\\ \\\\ // ${c1} \\\\ \\\\ //
==\\\\__\\\\/ // ==\\\\__\\\\/ //
// \\\\// // \\\\//
==// //== ==// //==
@@ -13001,39 +13045,39 @@ EOF
set_colors 11 set_colors 11
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
'',,, ,,,d, '',,, ,,,d,
',, ,,' ',, ,,'
', ,. ', ,.
., ' ., '
. . . .
' . ' .
.. oddddkdlc:;,.. .. .. oddddkdlc:;,.. ..
. ............lllc, . . ............lllc, .
. ....................: . . ....................: .
. . .................... . . ....................
'. ..........'o........d0XX0. '. ..........'o........d0XX0.
....lllllllcOOOcllllll............cxlxc...;okkkx. ....lllllllcOOOcllllll............cxlxc...;okkkx.
..................................';lc'...lo. ..................................';lc'...lo.
.'''''''''''''.....................,;,....... .'''''''''''''.....................,;,.......
',,,,,,,,,,,,,,,,''...............dkkkd...... ',,,,,,,,,,,,,,,,''...............dkkkd......
',,,,,,,,,,,,,,,,,,,'............;okkkd;.... ',,,,,,,,,,,,,,,,,,,'............;okkkd;....
.,,,,,,,,,,,,,,,,,,,,,............;cll;..... .,,,,,,,,,,,,,,,,,,,,,............;cll;.....
,,,,,,,,,,,,,,,,,,,,'....................:d, ,,,,,,,,,,,,,,,,,,,,'....................:d,
,,,,,,,,,,,,,,,,,,,....................oxxx: ,,,,,,,,,,,,,,,,,,,....................oxxx:
.,,,,,,,,,,,,,,,,,'..................oxxxxx. . .,,,,,,,,,,,,,,,,,'..................oxxxxx. .
.,,,,,,,,,,,,,,'.......... ,oxxxxxxx . .,,,,,,,,,,,,,,'.......... ,oxxxxxxx .
.;,,,,,,,,,,,,'.. 'cxxxxxxxxx, .;,,,,,,,,,,,,'.. 'cxxxxxxxxx,
:dl:,'',,'.... .;lxxxxxxxxxd; :dl:,'',,'.... .;lxxxxxxxxxd;
,codol:;'. ...,;cldxxxxxxxxxoc. ,codol:;'. ...,;cldxxxxxxxxxoc.
.:cxxxxxdlccccc:ccldxxxxxxxxxxxxxx::. .:cxxxxxdlccccc:ccldxxxxxxxxxxxxxx::.
.'::dxxxxxxxxxxxxxxxxxxxxxxxd::'. .'::dxxxxxxxxxxxxxxxxxxxxxxxd::'.
..,::cdxxxxxxxxxxxxxdc::,.. ..,::cdxxxxxxxxxxxxxdc::,..
...,;:::::::;,... ...,;:::::::;,...
EOF EOF
;; ;;
"phyOS"*) "phyOS"*)
set_colors 33 33 7 1 set_colors 33 33 7 1
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
@@ -13497,26 +13541,26 @@ EOF
"Reborn OS"* | "Reborn"*) "Reborn OS"* | "Reborn"*)
set_colors 0 4 6 set_colors 0 4 6
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} .======================. ${c1} .======================.
${c1}.#${c2}#*********${c1}%%${c2}*********#${c1}%: ${c1}.#${c2}#*********${c1}%%${c2}*********#${c1}%:
${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%- ${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%-
${c1}-%${c2}************${c1}%%${c2}************${c1}%= ${c1}-%${c2}************${c1}%%${c2}************${c1}%=
${c1}+%${c2}******${c1}%%#####${c1}%%#####%%${c2}******${c1}%+ ${c1}+%${c2}******${c1}%%#####${c1}%%#####%%${c2}******${c1}%+
${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%* ${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%*
${c1}*%${c2}*#${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##. ${c1}*%${c2}*#${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##.
${c1}.##${c2}*****${c1}#%%%#${c3}*++${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%. ${c1}.##${c2}*****${c1}#%%%#${c3}*++${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%.
${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%: ${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%:
${c1}-%#${c2}*****${c1}#%${c3}+====*${c1}%${c3}*++++++++*${c1}%#${c3}====+${c1}%#${c2}******${c1}%- ${c1}-%#${c2}*****${c1}#%${c3}+====*${c1}%${c3}*++++++++*${c1}%#${c3}====+${c1}%#${c2}******${c1}%-
${c1}-%#${c2}*****${c1}#%${c3}+====*${c1}%${c3}*++++++++*${c1}%#${c3}====+${c1}%#${c2}******${c1}%= ${c1}-%#${c2}*****${c1}#%${c3}+====*${c1}%${c3}*++++++++*${c1}%#${c3}====+${c1}%#${c2}******${c1}%=
${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%- ${c1}:%#${c2}*****${c1}#%${c3}*=+*${c1}#%%${c3}*++++++*${c1}%%#${c3}*+=*${c1}%#${c2}*****${c1}#%-
${c1}.##${c2}*****${c1}#%%%#${c3}*+*${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%: ${c1}.##${c2}*****${c1}#%%%#${c3}*+*${c1}%######%${c3}*+*${c1}#%%%#${c2}*****${c1}#%:
${c1}.##${c2}**${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##. ${c1}.##${c2}**${c1}#%%#%#${c3}====+++${c1}%%${c3}+++====${c1}#%#%%#${c2}#*${c1}##.
${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%* ${c1}*%%#${c2}****${c1}%#${c3}+=====${c1}%%${c3}=====+${c1}#%${c2}****${c1}#%%*
${c1}+%${c2}******${c1}%%#####%%#####%%${c2}******${c1}%* ${c1}+%${c2}******${c1}%%#####%%#####%%${c2}******${c1}%*
${c1}-%${c2}************${c1}%%${c2}************${c1}%= ${c1}-%${c2}************${c1}%%${c2}************${c1}%=
${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%- ${c1}:%${c2}#**********${c1}%%${c2}**********#${c1}%-
${c1}:%${c2}#*********${c1}%%${c2}*********#${c1}%: ${c1}:%${c2}#*********${c1}%%${c2}*********#${c1}%:
${c1}.======================. ${c1}.======================.
EOF EOF
;; ;;
@@ -13690,24 +13734,51 @@ EOF
"RhaymOS"*) "RhaymOS"*)
set_colors 1 set_colors 1
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
### ###
##### #####
####### /######## ####### /########
############# ########### ############# ###########
,########### #### ####(.. ,########### #### ####(..
#### #### ####* ########## #### #### ####* ##########
#### ##### ##### (#### #### ##### ##### (####
#### ########### ########### #### ########### ###########
#### ######### ########## #### ######### ##########
################################### ###################################
##################################### #####################################
####################################### #######################################
EOF EOF
;; ;;
"Rhino Linux"*)
set_colors 5 4
read -rd '' ascii_data <<'EOF'
${c1}
${c1} ..','.. l.
.,coooooooooc,..ool
;oooooooooooooooooooo.,coo.
;oooooooooooooooooooooooooo ,
.ooooooooooooooooooooooooooo . ,o
oollcccccccccllloooooooooooolcoo..oo
ll;':::::::::::::::ccloooooooooooooo:
.;''';::::::::::::::::::ccloooooooooo
''''',:::::::::::::::::::::::cclllllc
'''''';::::::::::::::::::::::::::::::
${c1}.'''''';::::::::::::::::${c2}ll${c1}:::::::::::
${c1} ''''''';:::::::::::::::${c2}d0${c1}::
${c1}.''''''',;:::::::${c2}cc${c1}::::${c2}0N:'
${c1}''''''''',,;::::${c2}cK${c1}:::${c2}dMX,
${c1}'''''''''''',,,${c2}dMccOMM${c1}:
${c1}.''''''''''''${c2}cWM0WMM${c1}.
${c1}'''''''':${c2}kMMMMM${c1}.
${c2}odxONMMMM
;Kl
EOF
;;
"rocky_small"*) "rocky_small"*)
set_colors 2 set_colors 2
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
@@ -14986,29 +15057,29 @@ oss${c2}yNMMMNyMMh${c1}sssssssssssssshmmmh${c1}ssssssso
.-\+oossssoo+/-. .-\+oossssoo+/-.
EOF EOF
;; ;;
"Floflis"*) "Floflis"*)
set_colors 1 7 3 set_colors 1 7 3
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
\e[96m ,▄▄▄▌▓▓███▓▓▌▄▄▄, \e[96m ,▄▄▄▌▓▓███▓▓▌▄▄▄,
,▄▒▓███████████████████▓▄▄ ,▄▒▓███████████████████▓▄▄
▄▓███████████████████████████▌ ▄▓███████████████████████████▌
▓███████████████████████████████ ▓███████████████████████████████
, ╙▓████████████████████████████▀ ▄ , ╙▓████████████████████████████▀ ▄
╓█▓▄ ╙▀▓████████████████████▀▀` ,▄██▓ ╓█▓▄ ╙▀▓████████████████████▀▀` ,▄██▓
╓█████▌▄, '▀▀▀▀▓▓▓▓▓▓▀▀Å╙` ▄▄▓█████▌ ╓█████▌▄, '▀▀▀▀▓▓▓▓▓▓▀▀Å╙` ▄▄▓█████▌
██████████▓▌▄ , ▄▓███████████▄ ██████████▓▌▄ , ▄▓███████████▄
╢████████████▓ ║████▓▓███▌ ╣█████████████▓ ╢████████████▓ ║████▓▓███▌ ╣█████████████▓
▓█████████████ ▐█████████▀ ▓██████████████ ▓█████████████ ▐█████████▀ ▓██████████████
▓█████████████ ▐█████████▄ ███████████████ ▓█████████████ ▐█████████▄ ███████████████
▀████████████▌ ║█████████▌ ▀█████████████▌ ▀████████████▌ ║█████████▌ ▀█████████████▌
████████████M ▓██████████ ▐█████████████⌐ ████████████M ▓██████████ ▐█████████████⌐
▀██████████▌ ▐███████████▌ ▀███████████▌ ▀██████████▌ ▐███████████▌ ▀███████████▌
╙▓█████▓ ▓██████████████▄ ▀███████▀ ╙▓█████▓ ▓██████████████▄ ▀███████▀
╝▓██▀ ╓▓████████████████▓ ▀▓██▀ ╝▓██▀ ╓▓████████████████▓ ▀▓██▀
,▄████████████████████▌, ,▄████████████████████▌,
╝▀████████████████████▓▀' ╝▀████████████████████▓▀'
`╙▀▀▓▓███████▓▀▀╩' `╙▀▀▓▓███████▓▀▀╩'
EOF EOF
;; ;;
@@ -15174,26 +15245,26 @@ EOF
set_colors 12 12 7 12 4 set_colors 12 12 7 12 4
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c3} :${c4}:::::::::::::: ${c5}. ${c3} :${c4}:::::::::::::: ${c5}.
${c3}=#${c4}*============. ${c5}:#: ${c3}=#${c4}*============. ${c5}:#:
${c3}=##%${c4}+----------. ${c5}.###: ${c3}=##%${c4}+----------. ${c5}.###:
${c3}=####. ${c5}.####: ${c3}=####. ${c5}.####:
${c3}=####. ${c5}.####: ${c3}=####. ${c5}.####:
${c3}=###*. .=${c4}--------. ${c5}####: ${c3}=###*. .=${c4}--------. ${c5}####:
${c3}=####. .*#+${c4}======- ${c5}####: ${c3}=####. .*#+${c4}======- ${c5}####:
${c3}=###*. .*###+${c4}====- ${c5}####: ${c3}=###*. .*###+${c4}====- ${c5}####:
${c3}=###*. .*#####+${c4}==- ${c5}####: ${c3}=###*. .*#####+${c4}==- ${c5}####:
${c3}=###*. .*#######+${c4}: ${c5}####: ${c3}=###*. .*#######+${c4}: ${c5}####:
${c3}=###*. .*#######+${c4}: ${c5}####: ${c3}=###*. .*#######+${c4}: ${c5}####:
${c3}=###*. .*#####+${c4}==- ${c5}####: ${c3}=###*. .*#####+${c4}==- ${c5}####:
${c3}=###*. .*###+${c4}====- ${c5}####: ${c3}=###*. .*###+${c4}====- ${c5}####:
${c3}=####. .*#+${c4}======- ${c5}####: ${c3}=####. .*#+${c4}======- ${c5}####:
${c3}=###*. .=${c4}--------. ${c5}.####: ${c3}=###*. .=${c4}--------. ${c5}.####:
${c3}=####. ${c5}.####: ${c3}=####. ${c5}.####:
${c3}=####. ${c5}.####: ${c3}=####. ${c5}.####:
${c3}=###+${c4}--------------${c5}####: ${c3}=###+${c4}--------------${c5}####:
${c3}=#+${c4}=================-${c5}: ${c3}=#+${c4}=================-${c5}:
${c3}:${c4}::::::::::::::::::. ${c3}:${c4}::::::::::::::::::.
EOF EOF
;; ;;
@@ -15219,25 +15290,25 @@ EOF
set_colors 8 4 set_colors 8 4
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
`-` `-`
-yys+/-` -yys+/-`
`oyyyyy: /osyyyyso+:. `oyyyyy: /osyyyyso+:.
/yyyyy+`+yyyyyyyyyys/. /yyyyy+`+yyyyyyyyyys/.
.-yyyyys.:+//+oyyyyyyyo. .-yyyyys.:+//+oyyyyyyyo.
`oy/`oyyyyy/ ./syyyyy: `oy/`oyyyyy/ ./syyyyy:
syyys`:yyyyyo` :yyyyy: syyys`:yyyyyo` :yyyyy:
/yyyyo .syyyyy- .yyyyy. /yyyyo .syyyyy- .yyyyy.
yyyyy. +yyyyy/ /yyyy/ yyyyy. +yyyyy/ /yyyy/
`yyyyy :yyyyys` -yyyyo `yyyyy :yyyyys` -yyyyo
yyyyy. `syyyyy- /yyyy/ yyyyy. `syyyyy- /yyyy/
/yyyyo /yyyyy+ .yyyyy. /yyyyo /yyyyy+ .yyyyy.
syyyys. -yyyyys.:yyyy: syyyys. -yyyyys.:yyyy:
`oyyyyyo-` `oyyyyy:.sy: `oyyyyyo-` `oyyyyy:.sy:
:syyyyyyso+/++`/yyyyyo`` :syyyyyyso+/++`/yyyyyo``
-oyyyyyyyyyyy-.syyyys. -oyyyyyyyyyyy-.syyyys.
-/+osyyyyso.`+yyyyy/ -/+osyyyyso.`+yyyyy/
.-/+syo` .-/+syo`
`. `.
EOF EOF
;; ;;
@@ -15738,21 +15809,21 @@ EOF
set_colors 4 7 set_colors 4 7
read -rd '' ascii_data <<'EOF' read -rd '' ascii_data <<'EOF'
${c1} ${c1}
. .:. . .:. . .:. . .:.
.^^.!.:::. .^!?J?^ .^^.!.:::. .^!?J?^
.:^.^!!!~:~^. .7??77!~~^. .:^.^!!!~:~^. .7??77!~~^.
.~^.!??77?!.^~: ..:^^7JJJ7~~^. .~^.!??77?!.^~: ..:^^7JJJ7~~^.
.^~.^7???7~.~~. .7??????????! .^~.^7???7~.~~. .7??????????!
.:^:^^~^^:!^ ^: .......^!:... .:^:^^~^^:!^ ^: .......^!:...
.!7~.::.!.::. ~BG~ :^ ^~: .!7~.::.!.::. ~BG~ :^ ^~:
:!!~ ~. ?BBBB! ^?J!. .!~. :!!~ ~. ?BBBB! ^?J!. .!~.
:!. .JBY. .Y#BBBY?~!???J7. :^^. :!. .JBY. .Y#BBBY?~!???J7. :^^.
.. :5#B#P~P#BBP?7?55J?J7: .. :5#B#P~P#BBP?7?55J?J7:
^P#BBBBBBBB5?7J5555J!..... ^P#BBBBBBBB5?7J5555J!.....
!BBBBBBGBBGJ77::Y555J?77777^ !BBBBBBGBBGJ77::Y555J?77777^
?BBBBG5JJ5PJ?!: .?Y??????77?~. ?BBBBG5JJ5PJ?!: .?Y??????77?~.
.YBGPYJ??????Y?^^^^~7?????????7?!. .YBGPYJ??????Y?^^^^~7?????????7?!.
.^^:..::::::::.....:::::::::::..:. .^^:..::::::::.....:::::::::::..:.
EOF EOF
;; ;;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "neowofetch", "name": "neowofetch",
"version": "1.99.0", "version": "2.0.2",
"description": "Updated neofetch", "description": "Updated neofetch",
"repository": { "repository": {
"type": "git", "type": "git",
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# This script does some file path magic to make cargo publish work without having
# to have neofetch & font logos in the cargo root.
# ...okay basically it copies these files over before publishing.
set -euo pipefail
# Get the directory of SRC root which is ../ from this script
SRC_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CARGO_ROOT="$SRC_ROOT/crates/hyfetch"
# Copy neofetch from here to cargo root
cp "$SRC_ROOT/neofetch" "$CARGO_ROOT/"
mkdir -p "$CARGO_ROOT/hyfetch"
cp -r "$SRC_ROOT/hyfetch/data" "$CARGO_ROOT/hyfetch/"
# Build the crate
cargo publish -p hyfetch --allow-dirty "$@"
# Delete the copied files
rm "$CARGO_ROOT/neofetch"
rm -rf "$CARGO_ROOT/hyfetch"
echo "Done!"
+20 -4
View File
@@ -51,8 +51,15 @@ def edit_versions(version: str):
path = Path('hyfetch/__version__.py') path = Path('hyfetch/__version__.py')
content = [f"VERSION = '{version}'" if l.startswith('VERSION = ') else l for l in path.read_text().split('\n')] content = [f"VERSION = '{version}'" if l.startswith('VERSION = ') else l for l in path.read_text().split('\n')]
path.write_text('\n'.join(content)) path.write_text('\n'.join(content))
# 3. Cargo.toml
print('Editing Cargo.toml...')
path = Path('Cargo.toml')
content = path.read_text()
content = re.sub(r'(?<=^version = ")[^"]+(?="$)', version, content, flags=re.MULTILINE)
path.write_text(content)
# 3. README.md # 4. README.md
print('Editing README.md...') print('Editing README.md...')
path = Path('README.md') path = Path('README.md')
content = path.read_text() content = path.read_text()
@@ -61,7 +68,7 @@ def edit_versions(version: str):
content = content[:changelog_i] + f'\n\n### {version}' + content[changelog_i:] content = content[:changelog_i] + f'\n\n### {version}' + content[changelog_i:]
path.write_text(content) path.write_text(content)
# 4. neofetch script # 5. neofetch script
print('Editing neofetch...') print('Editing neofetch...')
path = Path('neofetch') path = Path('neofetch')
lines = path.read_text().replace("\t", " ").split('\n') lines = path.read_text().replace("\t", " ").split('\n')
@@ -93,7 +100,7 @@ def finalize_neofetch():
# 2. Regenerate man page # 2. Regenerate man page
print('Regenerating neofetch man page...') print('Regenerating neofetch man page...')
Path('docs/neofetch.1').write_text(subprocess.check_output(['help2man', './neofetch']).decode()) Path('docs/neofetch.1').write_text(subprocess.check_output(['help2man', './neofetch']).decode())
Path('docs/hyfetch.1').write_text(subprocess.check_output(['help2man', 'python3 -m hyfetch']).decode()) Path('docs/hyfetch.1').write_text(subprocess.check_output(['help2man', 'cargo run --']).decode())
# 3. Reformat readme links # 3. Reformat readme links
print('Reformatting readme links...') print('Reformatting readme links...')
@@ -125,7 +132,12 @@ def create_release(v: str):
subprocess.check_call(['git', 'tag', f'neofetch-{NEOFETCH_NEW_VERSION}']) subprocess.check_call(['git', 'tag', f'neofetch-{NEOFETCH_NEW_VERSION}'])
i = input('Please check the commit is correct. Press y to continue or any other key to cancel.') i = input('Please check the commit is correct. Press y to continue or any other key to cancel.')
assert i == 'y' if i.lower() != 'y':
print('Aborting...')
subprocess.check_call(['git', 'reset', '--hard', 'HEAD~1'])
subprocess.check_call(['git', 'tag', '-d', v])
subprocess.check_call(['git', 'tag', '-d', f'neofetch-{NEOFETCH_NEW_VERSION}'])
exit(1)
# 4. Push # 4. Push
print('Pushing commits...') print('Pushing commits...')
@@ -140,6 +152,10 @@ def deploy():
print('Deploying to pypi...') print('Deploying to pypi...')
subprocess.check_call(['bash', 'tools/deploy.sh']) subprocess.check_call(['bash', 'tools/deploy.sh'])
print('Done!') print('Done!')
print('Deploying to crates.io...')
subprocess.check_call(['bash', 'tools/deploy-crate.sh'])
print('Done!')
print('Deploying to npm...') print('Deploying to npm...')
otp = input('Please provide 2FA OTP for NPM: ') otp = input('Please provide 2FA OTP for NPM: ')