Compare commits
370 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9806ced8b2 | |||
| 982ac52c69 | |||
| f9c9a92048 | |||
| b8e5dadd78 | |||
| 287921439d | |||
| e8e0a58e4b | |||
| 3a80670491 | |||
| 8cc06c02b5 | |||
| 48f52be2d3 | |||
| b009342d78 | |||
| 8e4b57e33a | |||
| 9a9101a9a3 | |||
| 90d12d9385 | |||
| ca74acbafb | |||
| afd9ad33d4 | |||
| cafa3ff752 | |||
| a0db219ba3 | |||
| 89cced9950 | |||
| 1ff4b12f19 | |||
| 4f8ddd1c22 | |||
| 7a42824883 | |||
| 916f7274df | |||
| 79fc190013 | |||
| e78d95e573 | |||
| 943503004d | |||
| deb2bddbe1 | |||
| 15fabbdb1a | |||
| df71b0132a | |||
| 191995c0fe | |||
| 6558cf39f1 | |||
| 99d652383a | |||
| e78ae3233c | |||
| a60dd35169 | |||
| ccc8121550 | |||
| bb260890c5 | |||
| 057b3b53e2 | |||
| 3a96fe8561 | |||
| 1b3b5ca883 | |||
| 53f7e3fcdb | |||
| e630b7837f | |||
| aa065fe910 | |||
| 28b365d418 | |||
| 4ea0a4a9dc | |||
| b5b49ecbc0 | |||
| cf56c60006 | |||
| a6225d698f | |||
| d5621877d5 | |||
| d5b93829fc | |||
| f3fc92dc7d | |||
| 36fc38c7a9 | |||
| 6af128fb05 | |||
| 8b3e4bba76 | |||
| 1211dc6978 | |||
| d0f911922a | |||
| c68dd7fe5b | |||
| 49b26dc7ea | |||
| f815d2bdd6 | |||
| b0b8c51d35 | |||
| 87e6092446 | |||
| fb52256729 | |||
| 5e11110c66 | |||
| a8b94f9187 | |||
| 3754aada04 | |||
| f94e788547 | |||
| a5f648a3fa | |||
| e184ed613a | |||
| f4d6b75f56 | |||
| 865850da59 | |||
| 54e0f55a08 | |||
| c969ce3a21 | |||
| afc646c679 | |||
| 6ac7f70c51 | |||
| c1fbd137a0 | |||
| 6f913b6d4a | |||
| 1077ee5142 | |||
| cec24f6bd0 | |||
| 9636c980e7 | |||
| 3866ad0249 | |||
| 0ca67a5608 | |||
| b2f7a3cee9 | |||
| b33b26625a | |||
| 1e67d65cec | |||
| e04ef3b0fd | |||
| a51a19658a | |||
| a65a229b0e | |||
| 5c0662142e | |||
| 88d835f511 | |||
| ce358fc155 | |||
| d5b283e64d | |||
| a609edc505 | |||
| 753d35b56e | |||
| d309f2d8a8 | |||
| e59a0556ed | |||
| afa18885ee | |||
| 69f07aec8f | |||
| 95a6cc7f4b | |||
| 896ba8fc7d | |||
| 319afd2aa9 | |||
| 3df77e0042 | |||
| 729e81a3e2 | |||
| 3dafba3554 | |||
| 36d87c2c35 | |||
| 0f744ff815 | |||
| 0dea6eb11b | |||
| 05685de193 | |||
| 03a6802b8f | |||
| d6341d241a | |||
| 32dd56dc63 | |||
| 6732945af8 | |||
| bb6b9b1d37 | |||
| 474f744bc6 | |||
| 7c139595a6 | |||
| ebd253523b | |||
| 13e29325b9 | |||
| 00369355e6 | |||
| 2865ee7131 | |||
| faa7ab240f | |||
| bcdc720d8a | |||
| 52844b55ad | |||
| 6ccf0d9c31 | |||
| e46e78ecd5 | |||
| 2f18cc2826 | |||
| 4c72ed6d79 | |||
| 55b0eed88d | |||
| 28f98a40fb | |||
| bbe40930c2 | |||
| a5913cc3d7 | |||
| f976781997 | |||
| cbe176ff4e | |||
| fe7ed923ea | |||
| c2795125d2 | |||
| 370b56c1db | |||
| f47561c947 | |||
| 89e5342614 | |||
| 4e3ec2604a | |||
| 279311fea0 | |||
| a7c277cb76 | |||
| c2586212d6 | |||
| 632296dbb4 | |||
| fe7d902dac | |||
| c7295ebed8 | |||
| bdd03a34af | |||
| 683d1fc2ec | |||
| c80a6185fc | |||
| 6564ca72c2 | |||
| 2a8cd48674 | |||
| 13ead437ec | |||
| 8edb50c4d8 | |||
| 14ff66b836 | |||
| 0c7c43d71c | |||
| 2613471978 | |||
| a558cf7ac9 | |||
| cd1858134c | |||
| 0a31cd0817 | |||
| e25684cfd8 | |||
| de43bf74a9 | |||
| d8f3302b89 | |||
| 5517283fb2 | |||
| 66b08f7a2e | |||
| 19e3b382b4 | |||
| e10bdac351 | |||
| 6e28482706 | |||
| 198742e15b | |||
| a5de091883 | |||
| da5f6ef655 | |||
| dbecd3d705 | |||
| e2d538df1f | |||
| 8514629960 | |||
| d11f6f0a9f | |||
| 45347e0714 | |||
| d79196a7fc | |||
| 114de9be12 | |||
| 4861744b50 | |||
| 66b57cfea6 | |||
| ca583bddcb | |||
| 965b009bc7 | |||
| 5b759e0986 | |||
| 3cc26d7dd9 | |||
| 85a85ad4d3 | |||
| f3b1840e92 | |||
| d773c69918 | |||
| 290da7c363 | |||
| fa66bb486b | |||
| 1239e86d1a | |||
| 66491a4e73 | |||
| c100ca52a6 | |||
| 46b618252f | |||
| be70233b03 | |||
| b5588d8259 | |||
| ff46c8f4ca | |||
| 59213687fb | |||
| 475b10ba07 | |||
| b0737a33ba | |||
| 3e4e679b11 | |||
| 3c355f54f2 | |||
| 86e442b8a4 | |||
| a0856aa4f5 | |||
| d1d99daa87 | |||
| 4f1efbc699 | |||
| c1b30918e9 | |||
| 517dde43cc | |||
| 955046c959 | |||
| 274440e9db | |||
| fd413caba4 | |||
| 474c339599 | |||
| 927803549e | |||
| 11601933f9 | |||
| b7f2992ecd | |||
| 1f1362a0b5 | |||
| 7c874a8e9a | |||
| 6e0e3fd18a | |||
| 974c4514b2 | |||
| e3b220a248 | |||
| ae1f6a2b3f | |||
| 290445110d | |||
| 4f9e6deb90 | |||
| 3b94b94528 | |||
| 39b44ad041 | |||
| bf2c3c51d4 | |||
| 2a01c50aa6 | |||
| 7f18d0e042 | |||
| 2307f9de20 | |||
| c185f018a4 | |||
| 9b159c7472 | |||
| ca77102be1 | |||
| 5ee89eb148 | |||
| ced109f0ea | |||
| 5051b692f0 | |||
| 07e23e2a5b | |||
| 270334a385 | |||
| df7884c2ac | |||
| a8f02e94b6 | |||
| 881833d3c9 | |||
| 207c608cd6 | |||
| ccab992c7b | |||
| e385eef74a | |||
| 820b78fb3b | |||
| 09c823d8c8 | |||
| aef326ae07 | |||
| 1139abd9c6 | |||
| 4517efc07e | |||
| 3b5b3a556b | |||
| 7534371b05 | |||
| fc98d61109 | |||
| 359226c26b | |||
| 109d7d2f01 | |||
| 33fd55e6ad | |||
| e08e864be0 | |||
| 07da5d5f4b | |||
| c2a26cf0ae | |||
| 1c1483822c | |||
| 46d38d4851 | |||
| a8c2cfb921 | |||
| d8affa54cd | |||
| 4707704330 | |||
| 882dafd11b | |||
| 9a867c6819 | |||
| bfeca1535b | |||
| b2afcb8a64 | |||
| 6e7ac1839d | |||
| 515afa90c3 | |||
| 1a3e6a8e29 | |||
| cff0fdf6d8 | |||
| 896658d5e1 | |||
| cdc0071e28 | |||
| 4ed4f19196 | |||
| 38b8173be4 | |||
| d70baebbf0 | |||
| a2abe13b86 | |||
| cd6f74931c | |||
| 1b1c90ec44 | |||
| d942de0b88 | |||
| d41a302e44 | |||
| 566011efff | |||
| 96ba35b0bf | |||
| 4c06a53c68 | |||
| 30d1e67b53 | |||
| 22c8d76193 | |||
| 36cdc6da2a | |||
| 293889b9ad | |||
| e21f9bef26 | |||
| bbe141557e | |||
| f06c3fda31 | |||
| dbe67789d8 | |||
| 6a1ba72df9 | |||
| 413a7d3681 | |||
| 18107d9601 | |||
| 5ded23e8dd | |||
| 09506aaa3c | |||
| 770ffb01a5 | |||
| 41d767fa4e | |||
| e5169705b3 | |||
| 73bb634326 | |||
| 8eadfec466 | |||
| 6816679ad9 | |||
| 2a87f1d3b4 | |||
| 6dec8c4a58 | |||
| e466389ae9 | |||
| bb5f45a71c | |||
| 3a415600d5 | |||
| 5bafa5128b | |||
| 51f0ebf3ea | |||
| c7d6c753d9 | |||
| d8bb0097c1 | |||
| 9292a602df | |||
| dbe657a05f | |||
| bb156173b5 | |||
| 9cebc9df35 | |||
| 25a64b6ce3 | |||
| 5c34876c53 | |||
| 1e30a7701f | |||
| 1c9480b764 | |||
| a385480d89 | |||
| 81f4dc90e8 | |||
| 2b6d17c559 | |||
| 6f9155fe83 | |||
| f32df6e6d0 | |||
| ece02c5d44 | |||
| d47a63d237 | |||
| be8f75df01 | |||
| bd9c0b30cf | |||
| b835eea039 | |||
| 01a01fb21b | |||
| 1bcf46aa42 | |||
| 811ec5d2d2 | |||
| dc359abe45 | |||
| be05f6984d | |||
| e6d94bf20d | |||
| f9a43fba5f | |||
| bc8499e2fb | |||
| 3ba7830824 | |||
| d914d3ea2b | |||
| 7eb4102417 | |||
| 7da1531d34 | |||
| b283b0d11d | |||
| b8115e97ae | |||
| 7c41025c55 | |||
| 98d3d9f629 | |||
| d374354cb6 | |||
| eeffb35944 | |||
| 048e2d93fd | |||
| b9db6d2cc5 | |||
| d5e3b07c1c | |||
| fd5ff6a821 | |||
| a33f85d45a | |||
| 1b89fc9531 | |||
| 2aa368ee9e | |||
| 3d6be24e9f | |||
| f2ed3e7155 | |||
| 98efa7a44d | |||
| 25ec6bbac5 | |||
| eef4153c5b | |||
| 17e02a384f | |||
| e50ec9e366 | |||
| 057f075a0e | |||
| a410eb0646 | |||
| 56796a331f | |||
| 2c33e7e37d | |||
| 8edddeaf8b | |||
| caa1547e8c | |||
| 807eed022d | |||
| b69c400fa4 | |||
| 5f266a3f16 | |||
| b740ddc3e5 | |||
| dfc3f59323 | |||
| 0c1ac93a4d | |||
| f449cfadc3 | |||
| bdb222f9f8 | |||
| 985f32e5a6 | |||
| da48f1d32c |
@@ -0,0 +1,2 @@
|
||||
[env]
|
||||
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||
@@ -0,0 +1,71 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.csv]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.rs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.sh]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.toml]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yaml,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[.github/{actions,workflows}/**/*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Cargo.toml]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[neofetch]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[package.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
+17
-2
@@ -1,2 +1,17 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
* text=auto eol=lf
|
||||
|
||||
*.csv text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.md text eol=lf
|
||||
*.py text eol=lf
|
||||
*.rs text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.toml text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.yml text eol=lf
|
||||
Cargo.lock text eol=lf merge=binary
|
||||
Makefile text eol=lf
|
||||
MANIFEST.in text eol=lf
|
||||
neofetch text eol=lf
|
||||
|
||||
@@ -120,3 +120,4 @@ webhook-log
|
||||
start_moderation.sh
|
||||
gh_moderator.toml
|
||||
moderator-data
|
||||
hyfetch/git
|
||||
Generated
+1209
File diff suppressed because it is too large
Load Diff
+51
@@ -0,0 +1,51 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.0.0-rc1"
|
||||
authors = ["Azalea Gui <azalea@hydev.org>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.75.0"
|
||||
description = "Neofetch with LGBTQ+ pride flags!"
|
||||
repository = "https://github.com/hykilpikonna/hyfetch"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
aho-corasick = { version = "1.1.3", default-features = false }
|
||||
ansi_colours = { version = "1.2.2", default-features = false }
|
||||
anstream = { version = "0.6.14", default-features = false }
|
||||
anyhow = { version = "1.0.86", default-features = false }
|
||||
bpaf = { version = "0.9.12", default-features = false }
|
||||
crossterm = { version = "0.27.0", default-features = false }
|
||||
deranged = { version = "0.3.11", default-features = false }
|
||||
directories = { version = "5.0.1", default-features = false }
|
||||
enable-ansi-support = { version = "0.2.1", default-features = false }
|
||||
enterpolation = { version = "0.2.1", default-features = false }
|
||||
fastrand = { version = "2.1.0", default-features = false }
|
||||
indexmap = { version = "2.2.6", default-features = false }
|
||||
itertools = { version = "0.13.0", default-features = false }
|
||||
normpath = { version = "1.2.0", default-features = false }
|
||||
palette = { version = "0.7.6", default-features = false }
|
||||
regex = { version = "1.10.5", default-features = false }
|
||||
same-file = { version = "1.0.6", default-features = false }
|
||||
serde = { version = "1.0.203", default-features = false }
|
||||
serde_json = { version = "1.0.118", default-features = false }
|
||||
serde_path_to_error = { version = "0.1.16", default-features = false }
|
||||
shell-words = { version = "1.1.0", default-features = false }
|
||||
strum = { version = "0.26.3", default-features = false }
|
||||
supports-color = { version = "3.0.0", default-features = false }
|
||||
tempfile = { version = "3.10.1", default-features = false }
|
||||
terminal-colorsaurus = { version = "0.4.3", default-features = false }
|
||||
terminal_size = { version = "0.3.0", default-features = false }
|
||||
thiserror = { version = "1.0.61", default-features = false }
|
||||
time = { version = "0.3.36", default-features = false }
|
||||
toml_edit = { version = "0.22.16", default-features = false }
|
||||
tracing = { version = "0.1.40", default-features = false }
|
||||
tracing-subscriber = { version = "0.3.18", default-features = false }
|
||||
unicode-normalization = { version = "0.1.23", default-features = false }
|
||||
unicode-segmentation = { version = "1.11.0", default-features = false }
|
||||
which = { version = "7.0.1", default-features = false }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
arithmetic_side_effects = "warn"
|
||||
@@ -1,35 +0,0 @@
|
||||
PREFIX = /usr
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
python setup.py build
|
||||
|
||||
install:
|
||||
python setup.py install --prefix=${PREFIX}
|
||||
|
||||
install-doc:
|
||||
@mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||
@cp -p docs/hyfetch.1 $(DESTDIR)$(MANDIR)/man1
|
||||
@cp -p docs/neofetch.1 $(DESTDIR)$(MANDIR)/man1/neowofetch.1
|
||||
|
||||
uninstall:
|
||||
@rm -rf $(DESTDIR)$(PREFIX)/bin/hyfetch
|
||||
@rm -rf $(DESTDIR)$(PREFIX)/bin/neowofetch
|
||||
@rm -rf $(DESTDIR)$(MANDIR)/man1/hyfetch.1*
|
||||
@rm -rf $(DESTDIR)$(MANDIR)/man1/neowofetch.1*
|
||||
|
||||
clean:
|
||||
rm -rf build/ HyFetch.egg-info
|
||||
|
||||
install-neofetch:
|
||||
@mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||
@mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||
@cp -p neofetch $(DESTDIR)$(PREFIX)/bin/neofetch
|
||||
@cp -p docs/neofetch.1 $(DESTDIR)$(MANDIR)/man1
|
||||
@chmod 755 $(DESTDIR)$(PREFIX)/bin/neofetch
|
||||
|
||||
uninstall-neofetch:
|
||||
@rm -rf $(DESTDIR)$(PREFIX)/bin/neofetch
|
||||
@rm -rf $(DESTDIR)$(MANDIR)/man1/neofetch.1*
|
||||
@@ -6,52 +6,57 @@ neofetch with pride flags <3
|
||||
|
||||
### Running Updated Original Neofetch
|
||||
|
||||
This repo also serves as an updated version of the original `neofetch` since the upstream [dylanaraps/neofetch](https://github.com/dylanaraps/neofetch) doesn't seem to be maintained anymore (as of Oct 27, 2023, the original repo hasn't merged a pull request for almost 2 years). If you only want to use the updated neofetch without pride flags, you can use the `neofetch` script from this repo. To prevent command name conflict, I call it `neowofetch` :)
|
||||
This repo also serves as an updated version of the original `neofetch` since the upstream [dylanaraps/neofetch](https://github.com/dylanaraps/neofetch) isn't maintained anymore and has been archived. If you only want to use the updated neofetch without pride flags, you can use the `neofetch` script from this repo. To prevent command name conflict, I call it `neowofetch` :)
|
||||
|
||||
* Method 1: `pip install -U hyfetch` then run `neowofetch`
|
||||
* Method 2: `npx neowofetch`
|
||||
* Method 3: `P="$HOME/.local/bin/neowofetch" curl -L nf.hydev.org -o $P && chmod +x $P`
|
||||
* Method 3: `P="$HOME/.local/bin/neowofetch" && curl -L nf.hydev.org -o "$P" && chmod +x "$P"`
|
||||
* Method 4: Run without install `bash <(curl -sL nf.hydev.org)`
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Method 1: Install using Python pip (Recommended)
|
||||
### Method 1: Install using pip (Recommended)
|
||||
|
||||
Install Python >= 3.7 first. Then, just do:
|
||||
Even though hyfetch has been rewritten in Rust since 2.0.0, we also published PyPI packages with prebuilt rust binaries to allow seamless transition from the Python version. You can install the rust binaries using pip:
|
||||
|
||||
```sh
|
||||
pip install wheel
|
||||
pip install -U hyfetch
|
||||
# or
|
||||
pipx install hyfetch
|
||||
```
|
||||
|
||||
If a rust binary is not available for your platform (e.g. riscv64), it will automatically fall back to the v1.99.0 Python version.
|
||||
|
||||
### Method 2: Install using system package manager
|
||||
|
||||
Currently, these distributions have existing packages for HyFetch:
|
||||
|
||||
* Universal [Lure.sh](https://lure.sh/): `lure in hyfetch` (Thanks to [@Elara6331](https://github.com/Elara6331))
|
||||
* Arch Linux: `sudo pacman -S hyfetch` (Thanks to [@Aleksana](https://github.com/Aleksanaa) and [@Antiz96](https://github.com/Antiz96))
|
||||
* Fedora Linux: `sudo dnf install hyfetch` (packaged by [@topazus](http://github.com/topazus))
|
||||
* Nix: `nix-env -i hyfetch` (Thanks to [@YisuiDenghua](https://github.com/YisuiDenghua))
|
||||
* Nix Profile: `nix profile install nixpkgs#hyfetch`
|
||||
* Guix: `guix install hyfetch` (Thanks to [@WammKD](https://github.com/WammKD))
|
||||
* Slackware: `sbopkg -b hyfetch` [Slackbuild](https://slackbuilds.org/repository/15.0/desktop/hyfetch/?search=hyfetch) (Thanks to [@bittin](https://github.com/bittin) and Urchlay)
|
||||
* Homebrew: `brew install hyfetch` (Thanks to [@BKasin](https://github.com/BKasin) and [@osalbahr](https://github.com/osalbahr))
|
||||
* openSUSE Tumbleweed: `zypper in python311-hyfetch` (Thanks to [@BKasin](https://github.com/BKasin))
|
||||
* Gentoo: `emerge --ask app-misc/hyfetch` (Thanks to [@BKasin](https://github.com/BKasin))
|
||||
* Slackware: `sbopkg -b hyfetch` [Slackbuild](https://slackbuilds.org/repository/15.0/desktop/hyfetch/?search=hyfetch) (Thanks to [@bittin](https://github.com/bittin) , willysr (https://github.com/willysr), jebrhansen and Urchlay)
|
||||
* Homebrew: `brew install hyfetch` (Thanks to [@catumin](https://github.com/catumin) and [@osalbahr](https://github.com/osalbahr))
|
||||
* openSUSE Tumbleweed: `zypper in python311-hyfetch` (Thanks to [@catumin](https://github.com/catumin))
|
||||
* Gentoo: `emerge --ask app-misc/hyfetch` (Thanks to [@catumin](https://github.com/catumin))
|
||||
* Debian and Ubuntu `apt install hyfetch` (for Debian flavor >= [Trixie](https://packages.debian.org/trixie/hyfetch), Ubuntu flavor >= [Noble](https://packages.ubuntu.com/noble/hyfetch)) (Thanks to [@catumin](https://github.com/catumin))
|
||||
|
||||
[](https://repology.org/project/hyfetch/versions)
|
||||
[](https://repology.org/project/hyfetch/versions)
|
||||
|
||||
### Method 3: Install the latest developmental version using git
|
||||
### Method 3: Install using Cargo
|
||||
|
||||
Install Python >= 3.7 first. Then run the following commands:
|
||||
Since hyfetch has been rewritten in Rust since 2.0.0, you can now install it using Cargo:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/hykilpikonna/hyfetch.git
|
||||
cd hyfetch
|
||||
pip install .
|
||||
cargo install hyfetch
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> If you install using Cargo, you would need to manually install dependencies like fastfetch if you want to use it as the backend, and Git Bash if you're on Windows.
|
||||
>
|
||||
> The PyPI package has all the dependencies bundled, so it's recommended to use that instead.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -82,7 +87,13 @@ For many in the LGBTQ+ community, these flags symbolize their identity, struggle
|
||||
|
||||
Also, by including flag coloring along with the updated neofetch, we're also broadcasting a wider message about the importance of inclusivity and representation. It's not just a design choice, it's a statement that promotes awareness and understanding toward the LGBTQ+ community.
|
||||
|
||||
#### Q: When I use `hyfetch` or `neowofetch` in my MotD, no art displays. How do I get the art back?
|
||||
|
||||
A: Most likely, the `stdout` detection is set to auto and is removing the ascii art and colors. To change this, you can: set `args` in your hyfetch.json to `"--stdout=off"`, add `--stdout=off` to the `neowofetch` command in your motd script, or set the option in $HOME/.config/(neofetch|neowofetch)/config.conf to off.
|
||||
|
||||
#### Q: How can I run HyFetch on Windows?
|
||||
|
||||
A: Install Python > 3.7 and Git > 2.42.0 first. Then, either open Git Bash terminal or install a terminal emulator that can display RGB colors (the default Windows cmd.exe cannot, but the new [Windows Terminal](https://apps.microsoft.com/detail/9N0DX20HK701?hl=en-US&gl=US) can). Then, run `pip install hyfetch` and run `hyfetch`.
|
||||
|
||||
|
||||
## Contributing
|
||||
@@ -118,14 +129,84 @@ pip install git+https://github.com/hykilpikonna/hyfetch.git@master
|
||||
|
||||
<!-- CHANGELOG STARTS HERE --->
|
||||
|
||||
### 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!
|
||||
|
||||
* 🌈 **Improve Windows support**
|
||||
* 🌈 **Include FastFetch into HyFetch PyPI package**
|
||||
* 🌈 Detached our fork from neofetch
|
||||
* 🌈 Build architecture-specific wheels for PyPI
|
||||
* 🌈 Add plural flag (bc8499e2)
|
||||
* 🌈 Add genderflux and girlflux flags ([#263](https://github.com/hykilpikonna/hyfetch/pull/263))
|
||||
* 🌈 Add Fraysexual flag ([#277](https://github.com/hykilpikonna/hyfetch/pull/277))
|
||||
* 🌈 Add Xenogender flag ([#309](https://github.com/hykilpikonna/hyfetch/pull/309))
|
||||
* 🌈 Add Caninekin flag ([#318](https://github.com/hykilpikonna/hyfetch/pull/318))
|
||||
* 🌈 Add Kenochoric, veldian, solian, lunian flags ([#331](https://github.com/hykilpikonna/hyfetch/pull/331))
|
||||
* 🌈 Add Polyamorous, sapphic, androgyne, interprogress, progress, intersex, equal-rights, drag, neofluid, genderqueer, and pronoun flags ([#342](https://github.com/hykilpikonna/hyfetch/pull/342))
|
||||
* 🌈 Add Gilbert Baker's original rainbow flag ([#284](https://github.com/hykilpikonna/hyfetch/pull/284))
|
||||
* 🌈 Add Queer subculture flags ([#302](https://github.com/hykilpikonna/hyfetch/pull/302))
|
||||
* 🌈 Add random flag option ([#334](https://github.com/hykilpikonna/hyfetch/pull/334))
|
||||
* 🌈 Fix distro logo triple-quotation mark escaping ([#222](https://github.com/hykilpikonna/hyfetch/pull/222))
|
||||
* 🌈 Fix Windows encoding issue on non-English systems ([#294](https://github.com/hykilpikonna/hyfetch/pull/294))
|
||||
* 🌈 Fix termux compatibility ([#286](https://github.com/hykilpikonna/hyfetch/pull/286))
|
||||
* 🌈 Fix term background detection not resetting properly ([#298](https://github.com/hykilpikonna/hyfetch/pull/298))
|
||||
* 🌈 Make typing-extensions optional ([#299](https://github.com/hykilpikonna/hyfetch/pull/299))
|
||||
* 🌈 Remove setuptools dependency ([#325](https://github.com/hykilpikonna/hyfetch/pull/325))
|
||||
* 🌈 Allow lightness value without a '%' sign in config prompt ([#307](https://github.com/hykilpikonna/hyfetch/pull/307))
|
||||
* 🌈 Fix Windows 7 and Python 3.7.0 support.
|
||||
* 🖼 OS - Update Apple hardware ID list ([#256](https://github.com/hykilpikonna/hyfetch/pull/256))
|
||||
* 🖼 OS - Remove Lilu detection for hackintosh ([#310](https://github.com/hykilpikonna/hyfetch/pull/310))
|
||||
* 🖼 OS - Support host info on PowerPC Macs ([#341](https://github.com/hykilpikonna/hyfetch/pull/341))
|
||||
* 🖼 DE - Show Kinfo for Plasma 6 ([#269](https://github.com/hykilpikonna/hyfetch/pull/269))
|
||||
* 🖼 GPU - Fix GPU not displaying in Haiku (dylanaraps#2448)
|
||||
* 🖼 GPU - List all intel GPU as detected ([#348](https://github.com/hykilpikonna/hyfetch/pull/348))
|
||||
* 🖼 Distro - Add eweOS ([#252](https://github.com/hykilpikonna/hyfetch/pull/252))
|
||||
* 🖼 Distro - Add Fedora Immutable distros (dylanaraps#2434)
|
||||
* 🖼 Distro - Add Macaroni OS (dylanaraps#2424)
|
||||
* 🖼 Distro - Add Ironclad OS ([#219](https://github.com/hykilpikonna/hyfetch/pull/219))
|
||||
* 🖼 Distro - Add Chimera Linux ([#285](https://github.com/hykilpikonna/hyfetch/pull/285))
|
||||
* 🖼 Distro - Add Tatra (dylanaraps#2439)
|
||||
* 🖼 Distro - Add Furreto Linux ([#290](https://github.com/hykilpikonna/hyfetch/pull/290))
|
||||
* 🖼 Distro - Add BlackMesa ([#316](https://github.com/hykilpikonna/hyfetch/pull/316))
|
||||
* 🖼 Distro - Add Magix ([#338](https://github.com/hykilpikonna/hyfetch/pull/338))
|
||||
* 🖼 Distro - Add Mauna ([#343](https://github.com/hykilpikonna/hyfetch/pull/343))
|
||||
* 🖼 Distro - Add Arkane Linux ([#321](https://github.com/hykilpikonna/hyfetch/pull/321))
|
||||
* 🖼 Distro - Add Linux From Scratch ([#336](https://github.com/hykilpikonna/hyfetch/pull/336))
|
||||
* 🖼 Distro - Fix debian version on Ubuntu ([#195](https://github.com/hykilpikonna/hyfetch/pull/195))
|
||||
* 🖼 Ascii - Update PikaOS logo ([#231](https://github.com/hykilpikonna/hyfetch/pull/231))
|
||||
* 🖼 Ascii - Add Fedora unicode logo ([#238](https://github.com/hykilpikonna/hyfetch/pull/238))
|
||||
* 🖼 Ascii - Fix colors not reverting properly ([#314](https://github.com/hykilpikonna/hyfetch/pull/314))
|
||||
* 🖼 Ascii - Fix AmogOS backslash escape ([#339](https://github.com/hykilpikonna/hyfetch/pull/339))
|
||||
* 🖼 Ascii - Add colorful NixOS logo ([#311](https://github.com/hykilpikonna/hyfetch/pull/311))
|
||||
* 🖼 Terminal - Fix terminal emulator selection ([#220](https://github.com/hykilpikonna/hyfetch/pull/220))
|
||||
* 🖼 Terminal - Fix terminal font info for yakuake ([#235](https://github.com/hykilpikonna/hyfetch/pull/235))
|
||||
* 🖼 Terminal - Add Apple Terminal ([#272](https://github.com/hykilpikonna/hyfetch/pull/272))
|
||||
* 🖼 Editor - Display neovim version ([#267](https://github.com/hykilpikonna/hyfetch/pull/267))
|
||||
* 🖼 Package - Add an option to hide some package managers ([#257](https://github.com/hykilpikonna/hyfetch/pull/257))
|
||||
* 🖼 Package - Add "AM" application manager ([#234](https://github.com/hykilpikonna/hyfetch/pull/234))
|
||||
* 🖼 Package - Fix npm list count mismatch ([#240](https://github.com/hykilpikonna/hyfetch/pull/240))
|
||||
* 🖼 Package - Add pnpm ([#288](https://github.com/hykilpikonna/hyfetch/pull/288))
|
||||
* 🖼 Package - Add pkgx.sh ([#265](https://github.com/hykilpikonna/hyfetch/pull/265))
|
||||
* 🖼 Package - Add pip, bonsai, rad, radula, birb ([#337](https://github.com/hykilpikonna/hyfetch/pull/337))
|
||||
* 🖼 Package - Add MacPorts for Linux support ([#340](https://github.com/hykilpikonna/hyfetch/pull/340))
|
||||
* 🖼 Package - Use XDG path for Nix package counting ([#255](https://github.com/hykilpikonna/hyfetch/pull/255))
|
||||
* 🖼 Package - Fix short package count ([#305](https://github.com/hykilpikonna/hyfetch/pull/305))
|
||||
* 🖼 Song - Add Cider player ([#245](https://github.com/hykilpikonna/hyfetch/pull/245))
|
||||
* 🖼 Network - Fix network speed detection on macOS ([#360](https://github.com/hykilpikonna/hyfetch/pull/360))
|
||||
|
||||
### 1.4.11
|
||||
|
||||
* 🌈 Add ability to set backend args in hyfetch config file ([#181](https://github.com/hykilpikonna/hyfetch/pull/181))
|
||||
* 🌈 Update makefile to be able to install hyfetch ([#174](https://github.com/hykilpikonna/hyfetch/pull/174))
|
||||
* 🌈 Fix config file argument ([#177](https://github.com/hykilpikonna/hyfetch/pull/177))
|
||||
* 🌈 Support pipx installation ([#188](https://github.com/hykilpikonna/hyfetch/pull/188), [#192](https://github.com/hykilpikonna/hyfetch/pull/192))
|
||||
* 🌈 Create package for Debian, OpenSUSE, Homebrew, Gentoo, and lure.sh
|
||||
([#184](https://github.com/hykilpikonna/hyfetch/pull/184), [#194](https://github.com/hykilpikonna/hyfetch/pull/194), [#207](https://github.com/hykilpikonna/hyfetch/pull/207), [#206](https://github.com/hykilpikonna/hyfetch/pull/206)) Huge thanks to @BKasin!
|
||||
([#184](https://github.com/hykilpikonna/hyfetch/pull/184), [#194](https://github.com/hykilpikonna/hyfetch/pull/194), [#207](https://github.com/hykilpikonna/hyfetch/pull/207), [#206](https://github.com/hykilpikonna/hyfetch/pull/206)) Huge thanks to @catumin!
|
||||
* 🖼 DE - Fix DE empty bracket in macOS ([#172](https://github.com/hykilpikonna/hyfetch/pull/172))
|
||||
* 🖼 OS - Disable Hackintosh check on arm64 processors ([dylanaraps#2396](https://github.com/dylanaraps/neofetch/pull/2396))
|
||||
* 🖼 OS - Add Windows NT ([#217](https://github.com/hykilpikonna/hyfetch/pull/217))
|
||||
* 🖼 Distro - Fixed Kubuntu recognized as Ubuntu ([dylanaraps#2411](https://github.com/dylanaraps/neofetch/pull/2411))
|
||||
* 🖼 Distro - Use /etc/debian_version to get .x on Debian ([#191](https://github.com/hykilpikonna/hyfetch/pull/191))
|
||||
* 🖼 Distro - Add LainOS ([#190](https://github.com/hykilpikonna/hyfetch/pull/190))
|
||||
* 🖼 Distro - Add aerOS ([dylanaraps#2360](https://github.com/dylanaraps/neofetch/pull/2360))
|
||||
@@ -143,10 +224,7 @@ pip install git+https://github.com/hykilpikonna/hyfetch.git@master
|
||||
* 🖼 Ascii - Add linux_small ([dylanaraps#2417](https://github.com/dylanaraps/neofetch/pull/2417))
|
||||
* 🖼 Bug Fix - Fix color blocks for bash !=3 & <5 ([#170](https://github.com/hykilpikonna/hyfetch/pull/170))
|
||||
* 🖼 Bug Fix - Use sed -r instead of -E when using GNU sed ([#171](https://github.com/hykilpikonna/hyfetch/pull/171))
|
||||
* 🖼 Bug Fix - Fixed Kubuntu recognized as Ubuntu ([dylanaraps#2411](https://github.com/dylanaraps/neofetch/pull/2411))
|
||||
* 🖼 OS - Improved MacOS resolution detection ([dylanaraps#2356](https://github.com/dylanaraps/neofetch/pull/2356))
|
||||
* 🖼 OS - Disable Hackintosh check on arm64 processors ([dylanaraps#2396](https://github.com/dylanaraps/neofetch/pull/2396))
|
||||
* 🖼 OS - Add Windows NT ([#217](https://github.com/hykilpikonna/hyfetch/pull/217))
|
||||
* 🖼 Resolution - Improved MacOS resolution detection ([dylanaraps#2356](https://github.com/dylanaraps/neofetch/pull/2356))
|
||||
* 🖼 Terminal - Add support for alacritty's new config format ([#202](https://github.com/hykilpikonna/hyfetch/pull/202))
|
||||
* 🖼 Terminal - Check for newer xfce4-term config ([#214](https://github.com/hykilpikonna/hyfetch/pull/214))
|
||||
* 🖼 Package - Add support for npm global packages ([#215](https://github.com/hykilpikonna/hyfetch/pull/215))
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
[package]
|
||||
name = "hyfetch"
|
||||
version = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
description = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
default-run = "hyfetch"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = { workspace = true, features = ["perf-literal", "std"] }
|
||||
ansi_colours = { workspace = true, features = [] }
|
||||
anstream = { workspace = true, features = [], optional = true }
|
||||
anyhow = { workspace = true, features = ["std"] }
|
||||
bpaf = { workspace = true, features = [] }
|
||||
crossterm = { workspace = true, features = [] }
|
||||
deranged = { workspace = true, features = ["serde", "std"] }
|
||||
directories = { workspace = true, features = [] }
|
||||
enterpolation = { workspace = true, features = ["bspline", "std"] }
|
||||
fastrand = { workspace = true, features = ["std"] }
|
||||
indexmap = { workspace = true, features = ["serde", "std"] }
|
||||
itertools = { workspace = true, features = ["use_std"] }
|
||||
palette = { workspace = true, features = ["std"] }
|
||||
serde = { workspace = true, features = ["derive", "std"] }
|
||||
serde_json = { workspace = true, features = ["std"] }
|
||||
serde_path_to_error = { workspace = true, features = [] }
|
||||
shell-words = { workspace = true, features = ["std"] }
|
||||
strum = { workspace = true, features = ["derive", "std"] }
|
||||
supports-color = { workspace = true, features = [] }
|
||||
tempfile = { workspace = true, features = [] }
|
||||
terminal-colorsaurus = { workspace = true, features = [] }
|
||||
terminal_size = { workspace = true, features = [] }
|
||||
thiserror = { workspace = true, features = [] }
|
||||
time = { workspace = true, features = ["local-offset", "std"] }
|
||||
toml_edit = { workspace = true, features = [], optional = true }
|
||||
tracing = { workspace = true, features = ["attributes", "std"] }
|
||||
tracing-subscriber = { workspace = true, features = ["ansi", "fmt", "smallvec", "std", "tracing-log"] }
|
||||
unicode-segmentation = { workspace = true, features = [] }
|
||||
which = { workspace = true, features = [] }
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = { workspace = true, features = ["std"] }
|
||||
regex = { workspace = true, features = ["perf", "std", "unicode"] }
|
||||
unicode-normalization = { workspace = true, features = ["std"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
enable-ansi-support = { workspace = true, features = [] }
|
||||
normpath = { workspace = true, features = [] }
|
||||
same-file = { workspace = true, features = [] }
|
||||
crossterm = { workspace = true, features = ["windows"] }
|
||||
|
||||
[features]
|
||||
default = ["autocomplete", "color", "macchina"]
|
||||
autocomplete = ["bpaf/autocomplete"]
|
||||
color = ["bpaf/dull-color"]
|
||||
macchina = ["dep:anstream", "dep:toml_edit", "toml_edit/display"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,257 @@
|
||||
use std::fmt::Write as _;
|
||||
use std::path::Path;
|
||||
use std::{env, fs};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use regex::Regex;
|
||||
use unicode_normalization::UnicodeNormalization as _;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AsciiDistro {
|
||||
pattern: String,
|
||||
art: String,
|
||||
}
|
||||
|
||||
impl AsciiDistro {
|
||||
fn friendly_name(&self) -> String {
|
||||
self.pattern
|
||||
.split('|')
|
||||
.next()
|
||||
.expect("invalid distro pattern")
|
||||
.trim_matches(|c: char| c.is_ascii_punctuation() || c == ' ')
|
||||
.replace(['"', '*'], "")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let neofetch_path = Path::new(env!("CARGO_WORKSPACE_DIR")).join("neofetch");
|
||||
|
||||
println!(
|
||||
"cargo:rerun-if-changed={neofetch_path}",
|
||||
neofetch_path = neofetch_path.display()
|
||||
);
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let out_path = Path::new(&out_dir);
|
||||
|
||||
export_distros(neofetch_path, out_path);
|
||||
}
|
||||
|
||||
fn export_distros<P>(neofetch_path: P, out_path: &Path)
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let distros = parse_ascii_distros(neofetch_path);
|
||||
let mut variants = IndexMap::with_capacity(distros.len());
|
||||
|
||||
for distro in &distros {
|
||||
let variant = distro
|
||||
.friendly_name()
|
||||
.replace(|c: char| c.is_ascii_punctuation() || c == ' ', "_")
|
||||
.nfc()
|
||||
.collect::<String>();
|
||||
if variants.contains_key(&variant) {
|
||||
let variant_fallback = format!("{variant}_fallback");
|
||||
if variants.contains_key(&variant_fallback) {
|
||||
todo!("too many name clashes in ascii distro patterns: {variant}");
|
||||
}
|
||||
variants.insert(variant_fallback, distro);
|
||||
continue;
|
||||
}
|
||||
variants.insert(variant, distro);
|
||||
}
|
||||
|
||||
let mut buf = r###"
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum Distro {
|
||||
"###
|
||||
.to_owned();
|
||||
|
||||
for (variant, AsciiDistro { pattern, .. }) in &variants {
|
||||
write!(
|
||||
buf,
|
||||
r###"
|
||||
// {pattern})
|
||||
{variant},
|
||||
"###,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
buf.push_str(
|
||||
r###"
|
||||
}
|
||||
|
||||
impl Distro {
|
||||
pub fn detect<S>(name: S) -> Option<Self>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let name = name.as_ref().to_lowercase();
|
||||
"###,
|
||||
);
|
||||
|
||||
for (variant, AsciiDistro { pattern, .. }) in &variants {
|
||||
let patterns = pattern.split('|').map(|s| s.trim());
|
||||
let mut conds = Vec::new();
|
||||
|
||||
for m in patterns {
|
||||
let stripped = m.trim_matches(['*', '\'', '"']).to_lowercase();
|
||||
|
||||
if stripped.contains(['*', '"']) {
|
||||
if let Some((prefix, suffix)) = stripped.split_once(r#""*""#) {
|
||||
conds.push(format!(
|
||||
r#"name.starts_with("{prefix}") && name.ends_with("{suffix}")"#
|
||||
));
|
||||
continue;
|
||||
}
|
||||
todo!("cannot properly parse: {m}");
|
||||
}
|
||||
|
||||
// Exact matches
|
||||
if m.trim_matches('*') == m {
|
||||
conds.push(format!(r#"name == "{stripped}""#));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both sides are *
|
||||
if m.starts_with('*') && m.ends_with('*') {
|
||||
conds.push(format!(
|
||||
r#"name.starts_with("{stripped}") || name.ends_with("{stripped}")"#
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ends with *
|
||||
if m.ends_with('*') {
|
||||
conds.push(format!(r#"name.starts_with("{stripped}")"#));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Starts with *
|
||||
if m.starts_with('*') {
|
||||
conds.push(format!(r#"name.ends_with("{stripped}")"#));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let condition = conds.join(" || ");
|
||||
|
||||
write!(
|
||||
buf,
|
||||
r###"
|
||||
if {condition} {{
|
||||
return Some(Self::{variant});
|
||||
}}
|
||||
"###
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
buf.push_str(
|
||||
r###"
|
||||
None
|
||||
}
|
||||
|
||||
pub fn ascii_art(&self) -> &str {
|
||||
let art = match self {
|
||||
"###,
|
||||
);
|
||||
|
||||
let quotes = "#".repeat(80);
|
||||
for (variant, AsciiDistro { art, .. }) in &variants {
|
||||
write!(
|
||||
buf,
|
||||
r###"
|
||||
Self::{variant} => r{quotes}"
|
||||
{art}
|
||||
"{quotes},
|
||||
"###,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
buf.push_str(
|
||||
r###"
|
||||
};
|
||||
&art[1..art.len().checked_sub(1).unwrap()]
|
||||
}
|
||||
}
|
||||
"###,
|
||||
);
|
||||
|
||||
fs::write(out_path.join("distros.rs"), buf).expect("couldn't write distros.rs");
|
||||
}
|
||||
|
||||
/// Parses ascii distros from neofetch script.
|
||||
fn parse_ascii_distros<P>(neofetch_path: P) -> Vec<AsciiDistro>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let neofetch_path = neofetch_path.as_ref();
|
||||
|
||||
let nf = {
|
||||
let nf = fs::read_to_string(neofetch_path).expect("couldn't read neofetch script");
|
||||
|
||||
// Get the content of "get_distro_ascii" function
|
||||
let (_, nf) = nf
|
||||
.split_once("get_distro_ascii() {\n")
|
||||
.expect("couldn't find get_distro_ascii function");
|
||||
let (nf, _) = nf
|
||||
.split_once("\n}\n")
|
||||
.expect("couldn't find end of get_distro_ascii function");
|
||||
|
||||
let mut nf = nf.replace('\t', &" ".repeat(4));
|
||||
|
||||
// Remove trailing spaces
|
||||
while nf.contains(" \n") {
|
||||
nf = nf.replace(" \n", "\n");
|
||||
}
|
||||
nf
|
||||
};
|
||||
|
||||
let case_re = Regex::new(r"case .*? in\n").expect("couldn't compile case regex");
|
||||
let eof_re = Regex::new(r"EOF[ \n]*?;;").expect("couldn't compile eof regex");
|
||||
|
||||
// Split by blocks
|
||||
let mut blocks = Vec::new();
|
||||
for b in case_re.split(&nf) {
|
||||
blocks.extend(eof_re.split(b).map(|sub| sub.trim()));
|
||||
}
|
||||
|
||||
// Parse blocks
|
||||
fn parse_block(block: &str) -> Option<AsciiDistro> {
|
||||
let (block, art) = block.split_once("'EOF'\n")?;
|
||||
|
||||
// Join \
|
||||
//
|
||||
// > A <backslash> that is not quoted shall preserve the literal value of the
|
||||
// > following character, with the exception of a <newline>. If a <newline>
|
||||
// > follows the <backslash>, the shell shall interpret this as line
|
||||
// > continuation. The <backslash> and <newline> shall be removed before
|
||||
// > splitting the input into tokens. Since the escaped <newline> is removed
|
||||
// > entirely from the input and is not replaced by any white space, it cannot
|
||||
// > serve as a token separator.
|
||||
// See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01
|
||||
let block = block.replace("\\\n", "");
|
||||
|
||||
// Get case pattern
|
||||
let pattern = block
|
||||
.split('\n')
|
||||
.next()
|
||||
.and_then(|pattern| pattern.trim().strip_suffix(')'))?;
|
||||
|
||||
// Unescape backslashes here because backslashes are escaped in neofetch
|
||||
// for printf
|
||||
let art = art.replace(r"\\", r"\");
|
||||
|
||||
Some(AsciiDistro {
|
||||
pattern: pattern.to_owned(),
|
||||
art,
|
||||
})
|
||||
}
|
||||
blocks
|
||||
.iter()
|
||||
.filter_map(|block| parse_block(block))
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write as _;
|
||||
use std::ops::Range;
|
||||
|
||||
use aho_corasick::AhoCorasick;
|
||||
use anyhow::{Context as _, Result};
|
||||
use indexmap::IndexMap;
|
||||
use tracing::debug;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::color_util::{
|
||||
color, ForegroundBackground, NeofetchAsciiIndexedColor, ToAnsiString as _,
|
||||
};
|
||||
use crate::neofetch_util::{
|
||||
ascii_size, ColorAlignment, NEOFETCH_COLORS_AC, NEOFETCH_COLOR_PATTERNS,
|
||||
};
|
||||
use crate::presets::ColorProfile;
|
||||
use crate::types::{AnsiMode, TerminalTheme};
|
||||
|
||||
/// Raw ascii art before any processing.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RawAsciiArt {
|
||||
pub asc: String,
|
||||
pub fg: Vec<NeofetchAsciiIndexedColor>,
|
||||
}
|
||||
|
||||
/// Normalized ascii art where every line has the same width.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NormalizedAsciiArt {
|
||||
pub lines: Vec<String>,
|
||||
pub w: u8,
|
||||
pub h: u8,
|
||||
pub fg: Vec<NeofetchAsciiIndexedColor>,
|
||||
}
|
||||
|
||||
/// Recolored ascii art with all color codes replaced.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RecoloredAsciiArt {
|
||||
pub lines: Vec<String>,
|
||||
pub w: u8,
|
||||
pub h: u8,
|
||||
}
|
||||
|
||||
impl RawAsciiArt {
|
||||
/// Makes sure every line is the same width.
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
pub fn to_normalized(&self) -> Result<NormalizedAsciiArt> {
|
||||
debug!("normalize ascii");
|
||||
|
||||
let (w, h) = ascii_size(&self.asc).context("failed to get ascii size")?;
|
||||
|
||||
let lines = self
|
||||
.asc
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let (line_w, _) = ascii_size(line).unwrap();
|
||||
let pad = " ".repeat(usize::from(w.checked_sub(line_w).unwrap()));
|
||||
format!("{line}{pad}")
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(NormalizedAsciiArt {
|
||||
lines,
|
||||
w,
|
||||
h,
|
||||
fg: self.fg.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl NormalizedAsciiArt {
|
||||
/// Uses a color alignment to recolor the ascii art.
|
||||
#[tracing::instrument(level = "debug", skip(self), fields(self.w = self.w, self.h = self.h))]
|
||||
pub fn to_recolored(
|
||||
&self,
|
||||
color_align: &ColorAlignment,
|
||||
color_profile: &ColorProfile,
|
||||
color_mode: AnsiMode,
|
||||
theme: TerminalTheme,
|
||||
) -> Result<RecoloredAsciiArt> {
|
||||
debug!("recolor ascii");
|
||||
|
||||
if self.lines.is_empty() {
|
||||
return Ok(RecoloredAsciiArt {
|
||||
lines: self.lines.clone(),
|
||||
w: 0,
|
||||
h: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let reset = color("&~&*", color_mode).expect("color reset should not be invalid");
|
||||
|
||||
let lines = match (color_align, self) {
|
||||
(ColorAlignment::Horizontal, Self { fg, .. }) => {
|
||||
let Self { lines, .. } = self
|
||||
.fill_starting()
|
||||
.context("failed to fill in starting neofetch color codes")?;
|
||||
|
||||
let ac = NEOFETCH_COLORS_AC
|
||||
.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
|
||||
|
||||
// Replace foreground colors
|
||||
let asc = {
|
||||
let asc = lines.join("\n");
|
||||
let mut replacements = NEOFETCH_COLOR_PATTERNS;
|
||||
let fg_color = color(
|
||||
match theme {
|
||||
TerminalTheme::Light => "&0",
|
||||
TerminalTheme::Dark => "&f",
|
||||
},
|
||||
color_mode,
|
||||
)
|
||||
.expect("foreground color should not be invalid");
|
||||
for &fore in fg {
|
||||
replacements[usize::from(u8::from(fore)).checked_sub(1).unwrap()] =
|
||||
&fg_color;
|
||||
}
|
||||
ac.replace_all(&asc, &replacements)
|
||||
};
|
||||
let lines = asc.lines();
|
||||
|
||||
// Add new colors
|
||||
let lines = {
|
||||
let ColorProfile { colors } = color_profile
|
||||
.with_length(self.h.try_into().expect("`h` should not be 0"))
|
||||
.with_context(|| {
|
||||
format!("failed to spread color profile to length {h}", h = self.h)
|
||||
})?;
|
||||
lines.enumerate().map(move |(i, line)| {
|
||||
let bg_color =
|
||||
colors[i].to_ansi_string(color_mode, ForegroundBackground::Foreground);
|
||||
const N: usize = NEOFETCH_COLOR_PATTERNS.len();
|
||||
let replacements = [&bg_color; N];
|
||||
ac.replace_all(line, &replacements)
|
||||
})
|
||||
};
|
||||
|
||||
// Reset colors at end of each line to prevent color bleeding
|
||||
lines.map(|line| format!("{line}{reset}")).collect()
|
||||
},
|
||||
(ColorAlignment::Vertical, Self { fg, .. }) if !fg.is_empty() => {
|
||||
if self.w == 0 {
|
||||
return Ok(RecoloredAsciiArt {
|
||||
lines: self.lines.clone(),
|
||||
w: 0,
|
||||
h: self.h,
|
||||
});
|
||||
}
|
||||
|
||||
let Self { lines, .. } = self
|
||||
.fill_starting()
|
||||
.context("failed to fill in starting neofetch color codes")?;
|
||||
|
||||
let color_profile = color_profile
|
||||
.with_length(self.w.try_into().expect("`w` should not be 0"))
|
||||
.with_context(|| {
|
||||
format!("failed to spread color profile to length {w}", w = self.w)
|
||||
})?;
|
||||
|
||||
// Apply colors
|
||||
let ac = NEOFETCH_COLORS_AC
|
||||
.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
|
||||
lines
|
||||
.into_iter()
|
||||
.map(|line| {
|
||||
let line: &str = line.as_ref();
|
||||
|
||||
// `AhoCorasick` operates on bytes; we need to map that back to grapheme
|
||||
// clusters (i.e. a character as seen on the terminal)
|
||||
// See https://github.com/BurntSushi/aho-corasick/issues/72#issuecomment-821128859
|
||||
let byte_idx_to_grapheme_idx: IndexMap<usize, usize> = {
|
||||
let mut m: IndexMap<_, _> = line
|
||||
.grapheme_indices(true)
|
||||
.enumerate()
|
||||
.map(|(gr_idx, (byte_idx, _))| (byte_idx, gr_idx))
|
||||
.collect();
|
||||
// Add an extra entry at the end, to support lookup using exclusive
|
||||
// range end
|
||||
m.insert(line.len(), m.len());
|
||||
m
|
||||
};
|
||||
|
||||
let mut matches = ac.find_iter(line).peekable();
|
||||
let mut dst = String::new();
|
||||
let mut offset: u8 = 0;
|
||||
loop {
|
||||
let current = matches.next();
|
||||
let next = matches.peek();
|
||||
let (neofetch_color_idx, span, done) = match (current, next) {
|
||||
(Some(m), Some(m_next)) => {
|
||||
let ai_start = m.start().checked_add(3).unwrap();
|
||||
let ai_end = m.end().checked_sub(1).unwrap();
|
||||
let neofetch_color_idx: NeofetchAsciiIndexedColor = line
|
||||
[ai_start..ai_end]
|
||||
.parse()
|
||||
.expect("neofetch color index should be valid");
|
||||
if offset == 0 && m.start() > 0 {
|
||||
dst.push_str(&line[..m.start()]);
|
||||
}
|
||||
offset =
|
||||
offset.checked_add(u8::try_from(m.len()).unwrap()).unwrap();
|
||||
let mut span = m.span();
|
||||
span.start = m.end();
|
||||
span.end = m_next.start();
|
||||
(neofetch_color_idx, span, false)
|
||||
},
|
||||
(Some(m), None) => {
|
||||
// Last color code
|
||||
let ai_start = m.start().checked_add(3).unwrap();
|
||||
let ai_end = m.end().checked_sub(1).unwrap();
|
||||
let neofetch_color_idx: NeofetchAsciiIndexedColor = line
|
||||
[ai_start..ai_end]
|
||||
.parse()
|
||||
.expect("neofetch color index should be valid");
|
||||
if offset == 0 && m.start() > 0 {
|
||||
dst.push_str(&line[..m.start()]);
|
||||
}
|
||||
offset =
|
||||
offset.checked_add(u8::try_from(m.len()).unwrap()).unwrap();
|
||||
let mut span = m.span();
|
||||
span.start = m.end();
|
||||
span.end = line.len();
|
||||
(neofetch_color_idx, span, true)
|
||||
},
|
||||
(None, _) => {
|
||||
// No color code in the entire line
|
||||
unreachable!(
|
||||
"`fill_starting` ensured each line of ascii art starts \
|
||||
with neofetch color code"
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
if span.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let txt = &line[span];
|
||||
|
||||
if fg.contains(&neofetch_color_idx) {
|
||||
let fore = color(
|
||||
match theme {
|
||||
TerminalTheme::Light => "&0",
|
||||
TerminalTheme::Dark => "&f",
|
||||
},
|
||||
color_mode,
|
||||
)
|
||||
.expect("foreground color should not be invalid");
|
||||
write!(dst, "{fore}{txt}{reset}").unwrap();
|
||||
} else {
|
||||
let mut c_range: Range<usize> = span.into();
|
||||
c_range.start = byte_idx_to_grapheme_idx
|
||||
.get(&c_range.start)
|
||||
.unwrap()
|
||||
.checked_sub(usize::from(offset))
|
||||
.unwrap();
|
||||
c_range.end = byte_idx_to_grapheme_idx
|
||||
.get(&c_range.end)
|
||||
.unwrap()
|
||||
.checked_sub(usize::from(offset))
|
||||
.unwrap();
|
||||
dst.push_str(
|
||||
&ColorProfile::new(Vec::from(&color_profile.colors[c_range]))
|
||||
.color_text(
|
||||
txt,
|
||||
color_mode,
|
||||
ForegroundBackground::Foreground,
|
||||
false,
|
||||
)
|
||||
.context("failed to color text using color profile")?,
|
||||
);
|
||||
}
|
||||
|
||||
if done {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(dst)
|
||||
})
|
||||
.collect::<Result<_>>()?
|
||||
},
|
||||
(ColorAlignment::Vertical, Self { fg, .. }) if fg.is_empty() => {
|
||||
// Remove existing colors
|
||||
let asc = {
|
||||
let asc = self.lines.join("\n");
|
||||
let ac = NEOFETCH_COLORS_AC
|
||||
.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
|
||||
const N: usize = NEOFETCH_COLOR_PATTERNS.len();
|
||||
const REPLACEMENTS: [&str; N] = [""; N];
|
||||
ac.replace_all(&asc, &REPLACEMENTS)
|
||||
};
|
||||
let lines = asc.lines();
|
||||
|
||||
// Add new colors
|
||||
lines
|
||||
.map(|line| {
|
||||
let line = color_profile
|
||||
.color_text(line, color_mode, ForegroundBackground::Foreground, false)
|
||||
.context("failed to color text using color profile")?;
|
||||
Ok(line)
|
||||
})
|
||||
.collect::<Result<_>>()?
|
||||
},
|
||||
(
|
||||
ColorAlignment::Custom {
|
||||
colors: custom_colors,
|
||||
},
|
||||
_,
|
||||
) => {
|
||||
let Self { lines, .. } = self
|
||||
.fill_starting()
|
||||
.context("failed to fill in starting neofetch color codes")?;
|
||||
|
||||
let ColorProfile { colors } = color_profile.unique_colors();
|
||||
|
||||
// Apply colors
|
||||
let asc = {
|
||||
let asc = lines.join("\n");
|
||||
let ac = NEOFETCH_COLORS_AC
|
||||
.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
|
||||
const N: usize = NEOFETCH_COLOR_PATTERNS.len();
|
||||
let mut replacements = vec![Cow::from(""); N];
|
||||
for (&ai, &pi) in custom_colors {
|
||||
let ai: u8 = ai.into();
|
||||
let pi: u8 = pi.into();
|
||||
replacements[usize::from(ai.checked_sub(1).unwrap())] = colors
|
||||
[usize::from(pi)]
|
||||
.to_ansi_string(color_mode, ForegroundBackground::Foreground)
|
||||
.into();
|
||||
}
|
||||
ac.replace_all(&asc, &replacements)
|
||||
};
|
||||
let lines = asc.lines();
|
||||
|
||||
// Reset colors at end of each line to prevent color bleeding
|
||||
lines.map(|line| format!("{line}{reset}")).collect()
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
},
|
||||
};
|
||||
|
||||
Ok(RecoloredAsciiArt {
|
||||
lines,
|
||||
w: self.w,
|
||||
h: self.h,
|
||||
})
|
||||
}
|
||||
|
||||
/// Fills the missing starting placeholders.
|
||||
///
|
||||
/// e.g. `"${c1}...\n..."` -> `"${c1}...\n${c1}..."`
|
||||
fn fill_starting(&self) -> Result<Self> {
|
||||
let ac =
|
||||
NEOFETCH_COLORS_AC.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
|
||||
|
||||
let mut last = None;
|
||||
let lines =
|
||||
self.lines
|
||||
.iter()
|
||||
.map(|line| {
|
||||
let line: &str = line.as_ref();
|
||||
|
||||
let mut new = String::new();
|
||||
let mut matches = ac.find_iter(line).peekable();
|
||||
|
||||
match matches.peek() {
|
||||
Some(m)
|
||||
if m.start() == 0
|
||||
|| line[0..m.start()].trim_end_matches(' ').is_empty() =>
|
||||
{
|
||||
// Line starts with neofetch color code
|
||||
last = Some(&line[m.span()]);
|
||||
},
|
||||
Some(_) => {
|
||||
new.push_str(last.context(
|
||||
"failed to find neofetch color code from a previous line",
|
||||
)?);
|
||||
},
|
||||
None => {
|
||||
new.push_str(last.unwrap_or(NEOFETCH_COLOR_PATTERNS[0]));
|
||||
},
|
||||
}
|
||||
new.push_str(line);
|
||||
|
||||
// Get the last placeholder for the next line
|
||||
if let Some(m) = matches.last() {
|
||||
last.context("non-space character seen before first color code")?;
|
||||
last = Some(&line[m.span()]);
|
||||
}
|
||||
|
||||
Ok(new)
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
Ok(Self {
|
||||
lines,
|
||||
fg: self.fg.clone(),
|
||||
..*self
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,254 @@
|
||||
use std::iter;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr as _;
|
||||
|
||||
use anyhow::Context as _;
|
||||
#[cfg(feature = "autocomplete")]
|
||||
use bpaf::ShellComp;
|
||||
use bpaf::{construct, long, OptionParser, Parser as _};
|
||||
use directories::BaseDirs;
|
||||
use itertools::Itertools as _;
|
||||
use strum::{VariantArray, VariantNames};
|
||||
|
||||
use crate::color_util::{color, Lightness};
|
||||
use crate::presets::Preset;
|
||||
use crate::types::{AnsiMode, Backend};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Options {
|
||||
pub config: bool,
|
||||
pub config_file: PathBuf,
|
||||
pub preset: Option<Preset>,
|
||||
pub mode: Option<AnsiMode>,
|
||||
pub backend: Option<Backend>,
|
||||
pub args: Option<Vec<String>>,
|
||||
pub scale: Option<f32>,
|
||||
pub lightness: Option<Lightness>,
|
||||
pub june: bool,
|
||||
pub debug: bool,
|
||||
pub distro: Option<String>,
|
||||
pub ascii_file: Option<PathBuf>,
|
||||
pub print_font_logo: bool,
|
||||
pub test_print: bool,
|
||||
pub ask_exit: bool,
|
||||
}
|
||||
|
||||
pub fn options() -> OptionParser<Options> {
|
||||
let config = long("config").short('c').help("Configure hyfetch").switch();
|
||||
let config_file = long("config-file")
|
||||
.short('C')
|
||||
.help("Use another config file")
|
||||
.argument("CONFIG_FILE");
|
||||
#[cfg(feature = "autocomplete")]
|
||||
let config_file = config_file.complete_shell(ShellComp::Nothing);
|
||||
let config_file = config_file
|
||||
.fallback_with(|| {
|
||||
Ok::<_, anyhow::Error>(
|
||||
BaseDirs::new()
|
||||
.context("failed to get base dirs")?
|
||||
.config_dir()
|
||||
.join("hyfetch.json"),
|
||||
)
|
||||
})
|
||||
.debug_fallback();
|
||||
let preset = long("preset")
|
||||
.short('p')
|
||||
.help(&*format!(
|
||||
"Use preset
|
||||
PRESET={{{presets}}}",
|
||||
presets = <Preset as VariantNames>::VARIANTS
|
||||
.iter()
|
||||
.chain(iter::once(&"random"))
|
||||
.join(",")
|
||||
))
|
||||
.argument::<String>("PRESET");
|
||||
#[cfg(feature = "autocomplete")]
|
||||
let preset = preset.complete(complete_preset);
|
||||
let preset = preset
|
||||
.parse(|s| {
|
||||
Preset::from_str(&s)
|
||||
.or_else(|e| {
|
||||
if s == "random" {
|
||||
let mut rng = fastrand::Rng::new();
|
||||
Ok(*rng
|
||||
.choice(<Preset as VariantArray>::VARIANTS)
|
||||
.expect("preset iterator should not be empty"))
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"PRESET should be one of {{{presets}}}",
|
||||
presets = <Preset as VariantNames>::VARIANTS
|
||||
.iter()
|
||||
.chain(iter::once(&"random"))
|
||||
.join(",")
|
||||
)
|
||||
})
|
||||
})
|
||||
.optional();
|
||||
let mode = long("mode")
|
||||
.short('m')
|
||||
.help(&*format!(
|
||||
"Color mode
|
||||
MODE={{{modes}}}",
|
||||
modes = AnsiMode::VARIANTS.join(",")
|
||||
))
|
||||
.argument::<String>("MODE");
|
||||
#[cfg(feature = "autocomplete")]
|
||||
let mode = mode.complete(complete_mode);
|
||||
let mode = mode
|
||||
.parse(|s| {
|
||||
AnsiMode::from_str(&s).with_context(|| {
|
||||
format!(
|
||||
"MODE should be one of {{{modes}}}",
|
||||
modes = AnsiMode::VARIANTS.join(",")
|
||||
)
|
||||
})
|
||||
})
|
||||
.optional();
|
||||
let backend = long("backend")
|
||||
.short('b')
|
||||
.help(&*format!(
|
||||
"Choose a *fetch backend
|
||||
BACKEND={{{backends}}}",
|
||||
backends = Backend::VARIANTS.join(",")
|
||||
))
|
||||
.argument::<String>("BACKEND");
|
||||
#[cfg(feature = "autocomplete")]
|
||||
let backend = backend.complete(complete_backend);
|
||||
let backend = backend
|
||||
.parse(|s| {
|
||||
Backend::from_str(&s).with_context(|| {
|
||||
format!(
|
||||
"BACKEND should be one of {{{backends}}}",
|
||||
backends = Backend::VARIANTS.join(",")
|
||||
)
|
||||
})
|
||||
})
|
||||
.optional();
|
||||
let args = long("args")
|
||||
.help("Additional arguments pass-through to backend")
|
||||
.argument::<String>("ARGS")
|
||||
.parse(|s| shell_words::split(&s).context("ARGS should be valid command-line arguments"))
|
||||
.optional();
|
||||
let scale = long("c-scale")
|
||||
.help("Lighten colors by a multiplier")
|
||||
.argument("SCALE")
|
||||
.optional();
|
||||
let lightness = long("c-set-l")
|
||||
.help("Set lightness value of the colors")
|
||||
.argument("LIGHTNESS")
|
||||
.optional();
|
||||
let june = long("june").help("Show pride month easter egg").switch();
|
||||
let debug = long("debug").help("Debug mode").switch();
|
||||
let distro = long("distro")
|
||||
.help("Test for a specific distro")
|
||||
.argument("DISTRO")
|
||||
.optional();
|
||||
let test_distro = long("test-distro")
|
||||
.help("Test for a specific distro")
|
||||
.argument("DISTRO")
|
||||
.optional();
|
||||
let distro = construct!([distro, test_distro]);
|
||||
let ascii_file = long("ascii-file")
|
||||
.help("Use a specific file for the ascii art")
|
||||
.argument("ASCII_FILE");
|
||||
#[cfg(feature = "autocomplete")]
|
||||
let ascii_file = ascii_file.complete_shell(ShellComp::Nothing);
|
||||
let ascii_file = ascii_file.optional();
|
||||
let print_font_logo = long("print-font-logo")
|
||||
.help("Print the Font Logo / Nerd Font icon of your distro and exit")
|
||||
.switch();
|
||||
// hidden
|
||||
let test_print = long("test-print")
|
||||
.help("Print the ascii distro and exit")
|
||||
.switch()
|
||||
.hide();
|
||||
let ask_exit = long("ask-exit")
|
||||
.help("Ask for input before exiting")
|
||||
.switch()
|
||||
.hide();
|
||||
|
||||
construct!(Options {
|
||||
config,
|
||||
config_file,
|
||||
preset,
|
||||
mode,
|
||||
backend,
|
||||
args,
|
||||
scale,
|
||||
lightness,
|
||||
june,
|
||||
debug,
|
||||
distro,
|
||||
ascii_file,
|
||||
print_font_logo,
|
||||
// hidden
|
||||
test_print,
|
||||
ask_exit,
|
||||
})
|
||||
.to_options()
|
||||
.header(
|
||||
&*color(
|
||||
"&l&bhyfetch&~&L - neofetch with flags <3",
|
||||
AnsiMode::Ansi256,
|
||||
)
|
||||
.expect("header should not contain invalid color codes"),
|
||||
)
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "autocomplete")]
|
||||
fn complete_preset(input: &String) -> Vec<(String, Option<String>)> {
|
||||
<Preset as VariantNames>::VARIANTS
|
||||
.iter()
|
||||
.chain(iter::once(&"random"))
|
||||
.filter_map(|&name| {
|
||||
if name.starts_with(input) {
|
||||
Some((name.to_owned(), None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[cfg(feature = "autocomplete")]
|
||||
fn complete_mode(input: &String) -> Vec<(String, Option<String>)> {
|
||||
AnsiMode::VARIANTS
|
||||
.iter()
|
||||
.filter_map(|&name| {
|
||||
if name.starts_with(input) {
|
||||
Some((name.to_owned(), None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[cfg(feature = "autocomplete")]
|
||||
fn complete_backend(input: &String) -> Vec<(String, Option<String>)> {
|
||||
Backend::VARIANTS
|
||||
.iter()
|
||||
.filter_map(|&name| {
|
||||
if name.starts_with(input) {
|
||||
Some((name.to_owned(), None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_options() {
|
||||
options().check_invariants(false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
use std::io::{self, Write as _};
|
||||
use std::num::{ParseFloatError, ParseIntError};
|
||||
use std::str::FromStr;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use aho_corasick::AhoCorasick;
|
||||
use ansi_colours::{ansi256_from_grey, rgb_from_ansi256, AsRGB as _};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use deranged::RangedU8;
|
||||
use palette::color_difference::ImprovedCiede2000 as _;
|
||||
use palette::{
|
||||
FromColor as _, IntoColor as _, IntoColorMut as _, Lab, LinSrgb, Okhsl, Srgb, SrgbLuma,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::types::{AnsiMode, TerminalTheme};
|
||||
|
||||
const MINECRAFT_COLORS: [(&str, &str); 30] = [
|
||||
// Minecraft formatting codes
|
||||
// ==========================
|
||||
("&0", "\x1b[38;5;0m"),
|
||||
("&1", "\x1b[38;5;4m"),
|
||||
("&2", "\x1b[38;5;2m"),
|
||||
("&3", "\x1b[38;5;6m"),
|
||||
("&4", "\x1b[38;5;1m"),
|
||||
("&5", "\x1b[38;5;5m"),
|
||||
("&6", "\x1b[38;5;3m"),
|
||||
("&7", "\x1b[38;5;7m"),
|
||||
("&8", "\x1b[38;5;8m"),
|
||||
("&9", "\x1b[38;5;12m"),
|
||||
("&a", "\x1b[38;5;10m"),
|
||||
("&b", "\x1b[38;5;14m"),
|
||||
("&c", "\x1b[38;5;9m"),
|
||||
("&d", "\x1b[38;5;13m"),
|
||||
("&e", "\x1b[38;5;11m"),
|
||||
("&f", "\x1b[38;5;15m"),
|
||||
("&l", "\x1b[1m"), // Enable bold text
|
||||
("&o", "\x1b[3m"), // Enable italic text
|
||||
("&n", "\x1b[4m"), // Enable underlined text
|
||||
("&k", "\x1b[8m"), // Enable hidden text
|
||||
("&m", "\x1b[9m"), // Enable strikethrough text
|
||||
("&r", "\x1b[0m"), // Reset everything
|
||||
// Extended codes (not officially in Minecraft)
|
||||
// ============================================
|
||||
("&-", "\n"), // Line break
|
||||
("&~", "\x1b[39m"), // Reset text color
|
||||
("&*", "\x1b[49m"), // Reset background color
|
||||
("&L", "\x1b[22m"), // Disable bold text
|
||||
("&O", "\x1b[23m"), // Disable italic text
|
||||
("&N", "\x1b[24m"), // Disable underlined text
|
||||
("&K", "\x1b[28m"), // Disable hidden text
|
||||
("&M", "\x1b[29m"), // Disable strikethrough text
|
||||
];
|
||||
const RGB_COLOR_PATTERNS: [&str; 2] = ["&gf(", "&gb("];
|
||||
|
||||
/// See https://github.com/mina86/ansi_colours/blob/b9feefce10def2ac632b215ecd20830a4fca7836/src/ansi256.rs#L109
|
||||
const ANSI256_GRAYSCALE_COLORS: [u8; 30] = [
|
||||
16, 59, 102, 145, 188, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
|
||||
245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
|
||||
];
|
||||
|
||||
static MINECRAFT_COLORS_AC: OnceLock<(AhoCorasick, Box<[&str; 30]>)> = OnceLock::new();
|
||||
static RGB_COLORS_AC: OnceLock<AhoCorasick> = OnceLock::new();
|
||||
|
||||
/// Represents the lightness component in [`Okhsl`].
|
||||
///
|
||||
/// The range of valid values is
|
||||
/// [`Lightness::MIN`]`..=`[`Lightness::MAX`]
|
||||
///
|
||||
/// [`Okhsl`]: palette::Okhsl
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Deserialize, Serialize)]
|
||||
pub struct Lightness(f32);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LightnessError {
|
||||
#[error(
|
||||
"invalid lightness {0}, expected value between {min} and {max}",
|
||||
min = Lightness::MIN,
|
||||
max = Lightness::MAX
|
||||
)]
|
||||
OutOfRange(f32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ParseLightnessError {
|
||||
#[error("invalid float")]
|
||||
InvalidFloat(#[from] ParseFloatError),
|
||||
#[error("invalid lightness")]
|
||||
InvalidLightness(#[from] LightnessError),
|
||||
}
|
||||
|
||||
/// An indexed color where the color palette is the set of colors used in
|
||||
/// neofetch ascii art.
|
||||
///
|
||||
/// The range of valid values as supported in neofetch is
|
||||
/// [`NeofetchAsciiIndexedColor::MIN`]`..=`[`NeofetchAsciiIndexedColor::MAX`]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
|
||||
pub struct NeofetchAsciiIndexedColor(
|
||||
RangedU8<{ NeofetchAsciiIndexedColor::MIN }, { NeofetchAsciiIndexedColor::MAX }>,
|
||||
);
|
||||
|
||||
/// An indexed color where the color palette is the set of unique colors in a
|
||||
/// preset.
|
||||
///
|
||||
/// The range of valid values depends on the number of unique colors in a
|
||||
/// certain preset.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
|
||||
pub struct PresetIndexedColor(u8);
|
||||
|
||||
/// Whether the color is for foreground text or background color.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum ForegroundBackground {
|
||||
Foreground,
|
||||
Background,
|
||||
}
|
||||
|
||||
pub trait ToAnsiString {
|
||||
/// Converts RGB to ANSI escape code.
|
||||
fn to_ansi_string(&self, mode: AnsiMode, foreground_background: ForegroundBackground)
|
||||
-> String;
|
||||
}
|
||||
|
||||
pub trait Theme {
|
||||
fn theme(&self) -> TerminalTheme;
|
||||
}
|
||||
|
||||
pub trait ContrastGrayscale {
|
||||
/// Calculates the grayscale foreground color which provides the highest
|
||||
/// contrast against this background color.
|
||||
///
|
||||
/// The returned color is one of the ANSI 256 (8-bit) grayscale colors.
|
||||
///
|
||||
/// See <https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg>
|
||||
fn contrast_grayscale(&self) -> SrgbLuma<u8>;
|
||||
}
|
||||
|
||||
impl Lightness {
|
||||
pub const MAX: f32 = 1.0f32;
|
||||
pub const MIN: f32 = 0.0f32;
|
||||
|
||||
pub fn new(value: f32) -> Result<Self, LightnessError> {
|
||||
if !(Self::MIN..=Self::MAX).contains(&value) {
|
||||
return Err(LightnessError::OutOfRange(value));
|
||||
}
|
||||
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<f32> for Lightness {
|
||||
type Error = LightnessError;
|
||||
|
||||
fn try_from(value: f32) -> Result<Self, Self::Error> {
|
||||
Lightness::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Lightness {
|
||||
type Err = ParseLightnessError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Lightness::new(s.parse()?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Lightness> for f32 {
|
||||
fn from(value: Lightness) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl NeofetchAsciiIndexedColor {
|
||||
pub const MAX: u8 = 6;
|
||||
pub const MIN: u8 = 1;
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for NeofetchAsciiIndexedColor {
|
||||
type Error = deranged::TryFromIntError;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for NeofetchAsciiIndexedColor {
|
||||
type Err = deranged::ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NeofetchAsciiIndexedColor> for u8 {
|
||||
fn from(value: NeofetchAsciiIndexedColor) -> Self {
|
||||
value.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for PresetIndexedColor {
|
||||
fn from(value: u8) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PresetIndexedColor {
|
||||
type Err = ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PresetIndexedColor> for u8 {
|
||||
fn from(value: PresetIndexedColor) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAnsiString for Srgb<u8> {
|
||||
fn to_ansi_string(
|
||||
&self,
|
||||
mode: AnsiMode,
|
||||
foreground_background: ForegroundBackground,
|
||||
) -> String {
|
||||
let c: u8 = match foreground_background {
|
||||
ForegroundBackground::Foreground => 38,
|
||||
ForegroundBackground::Background => 48,
|
||||
};
|
||||
match mode {
|
||||
AnsiMode::Rgb => {
|
||||
let [r, g, b]: [u8; 3] = (*self).into();
|
||||
format!("\x1b[{c};2;{r};{g};{b}m")
|
||||
},
|
||||
AnsiMode::Ansi256 => {
|
||||
let rgb: [u8; 3] = (*self).into();
|
||||
let indexed = rgb.to_ansi256();
|
||||
format!("\x1b[{c};5;{indexed}m")
|
||||
},
|
||||
AnsiMode::Ansi16 => {
|
||||
unimplemented!();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAnsiString for SrgbLuma<u8> {
|
||||
fn to_ansi_string(
|
||||
&self,
|
||||
mode: AnsiMode,
|
||||
foreground_background: ForegroundBackground,
|
||||
) -> String {
|
||||
let c: u8 = match foreground_background {
|
||||
ForegroundBackground::Foreground => 38,
|
||||
ForegroundBackground::Background => 48,
|
||||
};
|
||||
match mode {
|
||||
AnsiMode::Rgb => {
|
||||
let rgb_f32_color: LinSrgb = self.into_linear().into_color();
|
||||
let [r, g, b]: [u8; 3] = Srgb::<u8>::from_linear(rgb_f32_color).into();
|
||||
format!("\x1b[{c};2;{r};{g};{b}m")
|
||||
},
|
||||
AnsiMode::Ansi256 => {
|
||||
let indexed = ansi256_from_grey(self.luma);
|
||||
format!("\x1b[{c};5;{indexed}m")
|
||||
},
|
||||
AnsiMode::Ansi16 => {
|
||||
unimplemented!();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Theme for Srgb<u8> {
|
||||
fn theme(&self) -> TerminalTheme {
|
||||
let mut rgb_f32_color: LinSrgb = self.into_linear();
|
||||
|
||||
{
|
||||
let okhsl_f32_color: &mut Okhsl = &mut rgb_f32_color.into_color_mut();
|
||||
|
||||
if okhsl_f32_color.lightness > 0.5 {
|
||||
TerminalTheme::Light
|
||||
} else {
|
||||
TerminalTheme::Dark
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContrastGrayscale for Srgb<u8> {
|
||||
fn contrast_grayscale(&self) -> SrgbLuma<u8> {
|
||||
let self_lab_f32: Lab = self.into_linear().into_color();
|
||||
|
||||
let mut best_contrast = None;
|
||||
for indexed in ANSI256_GRAYSCALE_COLORS {
|
||||
let rgb_u8_color: Srgb<u8> = rgb_from_ansi256(indexed).into();
|
||||
let lab_f32_color: Lab = rgb_u8_color.into_linear().into_color();
|
||||
let diff = lab_f32_color.improved_difference(self_lab_f32);
|
||||
best_contrast = match best_contrast {
|
||||
Some((_, best_diff)) if diff > best_diff => Some((lab_f32_color, diff)),
|
||||
None => Some((lab_f32_color, diff)),
|
||||
best => best,
|
||||
};
|
||||
}
|
||||
let (best_lab_f32, _) = best_contrast.expect("`best_contrast` should not be `None`");
|
||||
SrgbLuma::from_color(best_lab_f32).into_format()
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces extended minecraft color codes in message.
|
||||
///
|
||||
/// Returns message with escape codes.
|
||||
pub fn color<S>(msg: S, mode: AnsiMode) -> Result<String>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let msg = msg.as_ref();
|
||||
|
||||
let msg = {
|
||||
let (ac, escape_codes) = MINECRAFT_COLORS_AC.get_or_init(|| {
|
||||
let (color_codes, escape_codes): (Vec<_>, Vec<_>) =
|
||||
MINECRAFT_COLORS.into_iter().unzip();
|
||||
let ac = AhoCorasick::new(color_codes).unwrap();
|
||||
(
|
||||
ac,
|
||||
escape_codes.try_into().expect(
|
||||
"`MINECRAFT_COLORS` should have the same number of elements as \
|
||||
`MINECRAFT_COLORS_AC.get_or_init(...).1`",
|
||||
),
|
||||
)
|
||||
});
|
||||
ac.replace_all(msg, &escape_codes[..])
|
||||
};
|
||||
|
||||
let ac = RGB_COLORS_AC.get_or_init(|| AhoCorasick::new(RGB_COLOR_PATTERNS).unwrap());
|
||||
let mut dst = String::new();
|
||||
let mut ret_err = None;
|
||||
ac.replace_all_with(&msg, &mut dst, |m, _, dst| {
|
||||
let start = m.end();
|
||||
let end = msg[start..]
|
||||
.find(')')
|
||||
.context("missing closing brace for color code");
|
||||
let end = match end {
|
||||
Ok(end) => end,
|
||||
Err(err) => {
|
||||
ret_err = Some(err);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
let code = &msg[start..end];
|
||||
let foreground_background = if m.pattern().as_usize() == 0 {
|
||||
ForegroundBackground::Foreground
|
||||
} else {
|
||||
ForegroundBackground::Background
|
||||
};
|
||||
|
||||
let rgb: Srgb<u8> = if code.starts_with('#') {
|
||||
let rgb = code.parse().context("failed to parse hex color");
|
||||
match rgb {
|
||||
Ok(rgb) => rgb,
|
||||
Err(err) => {
|
||||
ret_err = Some(err);
|
||||
return false;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
let rgb: Result<[&str; 3], _> = code
|
||||
.split(&[',', ';', ' '])
|
||||
.filter(|x| x.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.map_err(|_| anyhow!("wrong number of rgb components"));
|
||||
let rgb = match rgb {
|
||||
Ok(rgb) => rgb,
|
||||
Err(err) => {
|
||||
ret_err = Some(err);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
let rgb = rgb
|
||||
.into_iter()
|
||||
.map(u8::from_str)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("failed to parse rgb components");
|
||||
let rgb: [u8; 3] = match rgb {
|
||||
Ok(rgb) => rgb.try_into().unwrap(),
|
||||
Err(err) => {
|
||||
ret_err = Some(err);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
rgb.into()
|
||||
};
|
||||
|
||||
dst.push_str(&rgb.to_ansi_string(mode, foreground_background));
|
||||
|
||||
true
|
||||
});
|
||||
if let Some(err) = ret_err {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Prints with color.
|
||||
pub fn printc<S>(msg: S, mode: AnsiMode) -> Result<()>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
writeln!(
|
||||
io::stdout(),
|
||||
"{msg}",
|
||||
msg = color(format!("{msg}&r", msg = msg.as_ref()), mode)
|
||||
.context("failed to color message")?
|
||||
)
|
||||
.context("failed to write message to stdout")
|
||||
}
|
||||
|
||||
/// Clears screen using ANSI escape codes.
|
||||
pub fn clear_screen(title: Option<&str>, mode: AnsiMode, debug_mode: bool) -> Result<()> {
|
||||
if !debug_mode {
|
||||
write!(io::stdout(), "\x1b[2J\x1b[H")
|
||||
.and_then(|_| io::stdout().flush())
|
||||
.context("failed to write clear screen sequence to stdout")?;
|
||||
}
|
||||
|
||||
if let Some(title) = title {
|
||||
printc(format!("\n{title}\n"), mode).context("failed to print title")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/distros.rs"));
|
||||
@@ -0,0 +1,47 @@
|
||||
use crate::neofetch_util::get_distro_name;
|
||||
use crate::types::Backend;
|
||||
use crate::utils::get_cache_path;
|
||||
use anyhow::{Context, Result};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
const FONT_LOGOS: &str = include_str!("../../../hyfetch/data/font_logos.json");
|
||||
|
||||
pub fn get_font_logo(backend: Backend) -> Result<String> {
|
||||
// Check if the cache file exists and return its contents if it does
|
||||
let cache_path = get_cache_path().context("Failed to get cache path")?.join("font_logo");
|
||||
if cache_path.exists() {
|
||||
let mut cached_logo = String::new();
|
||||
File::open(cache_path).context("Failed to open cache file")?
|
||||
.read_to_string(&mut cached_logo).context("Failed to read from cache file")?;
|
||||
return Ok(cached_logo);
|
||||
}
|
||||
|
||||
// Deserialize the JSON into a HashMap
|
||||
let font_logos: HashMap<String, String> = serde_json::from_str::<HashMap<String, String>>(FONT_LOGOS)
|
||||
.context("Failed to deserialize font logos JSON file")?
|
||||
.into_iter().map(|(k, v)| (k.to_lowercase(), v)).collect();
|
||||
|
||||
// Get the distro name
|
||||
let distro = get_distro_name(backend).context("Failed to get distro name")?.to_lowercase();
|
||||
|
||||
// Find the most likely matching distro from font_logos
|
||||
let matched_distro = font_logos.keys().find(|&k| distro.contains(k))
|
||||
.or_else(|| font_logos.keys().find(|k| k.contains(&distro)))
|
||||
.or_else(|| font_logos.keys().find(|k| k.split_whitespace().any(|part| distro.contains(part))))
|
||||
.ok_or_else(|| anyhow::anyhow!("No font logo found for distro: {distro}. The supported logos are in https://github.com/Lukas-W/font-logos"))?;
|
||||
|
||||
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
|
||||
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")?;
|
||||
|
||||
Ok(logo.clone())
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
pub mod ascii;
|
||||
pub mod cli_options;
|
||||
pub mod color_util;
|
||||
pub mod distros;
|
||||
pub mod font_logo;
|
||||
pub mod models;
|
||||
pub mod neofetch_util;
|
||||
pub mod presets;
|
||||
pub mod pride_month;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
@@ -0,0 +1,123 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::color_util::Lightness;
|
||||
use crate::neofetch_util::ColorAlignment;
|
||||
use crate::presets::Preset;
|
||||
use crate::types::{AnsiMode, Backend, TerminalTheme};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub preset: Preset,
|
||||
pub mode: AnsiMode,
|
||||
pub light_dark: TerminalTheme,
|
||||
pub lightness: Option<Lightness>,
|
||||
pub color_align: ColorAlignment,
|
||||
pub backend: Backend,
|
||||
#[serde(default)]
|
||||
#[serde(with = "self::args_serde")]
|
||||
pub args: Option<Vec<String>>,
|
||||
pub distro: Option<String>,
|
||||
pub pride_month_disable: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default_lightness(theme: TerminalTheme) -> Lightness {
|
||||
match theme {
|
||||
TerminalTheme::Dark => {
|
||||
Lightness::new(0.65).expect("default lightness should not be invalid")
|
||||
},
|
||||
TerminalTheme::Light => {
|
||||
Lightness::new(0.4).expect("default lightness should not be invalid")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lightness(&self) -> Lightness {
|
||||
self.lightness
|
||||
.unwrap_or_else(|| Self::default_lightness(self.light_dark))
|
||||
}
|
||||
}
|
||||
|
||||
mod args_serde {
|
||||
use std::fmt;
|
||||
|
||||
use serde::de::{self, value, Deserialize, Deserializer, SeqAccess, Visitor};
|
||||
use serde::ser::Serializer;
|
||||
|
||||
type Value = Option<Vec<String>>;
|
||||
|
||||
pub(super) fn serialize<S>(value: &Value, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match value {
|
||||
Some(value) => serializer.serialize_some(&shell_words::join(value)),
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct StringOrVec;
|
||||
|
||||
struct OptionVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for StringOrVec {
|
||||
type Value = Vec<String>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("string or list of strings")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
shell_words::split(s).map_err(de::Error::custom)
|
||||
}
|
||||
|
||||
fn visit_seq<S>(self, seq: S) -> Result<Self::Value, S::Error>
|
||||
where
|
||||
S: SeqAccess<'de>,
|
||||
{
|
||||
Deserialize::deserialize(value::SeqAccessDeserializer::new(seq))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for OptionVisitor {
|
||||
type Value = Value;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("option")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(StringOrVec).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_option(OptionVisitor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,783 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(feature = "macchina")]
|
||||
use std::fs;
|
||||
use std::io::{self, Write as _};
|
||||
use std::path::{PathBuf};
|
||||
use std::process::Command;
|
||||
use std::sync::OnceLock;
|
||||
use std::{env, fmt};
|
||||
|
||||
use aho_corasick::AhoCorasick;
|
||||
use anyhow::{Context as _, Result};
|
||||
use indexmap::IndexMap;
|
||||
use itertools::Itertools as _;
|
||||
#[cfg(windows)]
|
||||
use anyhow::anyhow;
|
||||
#[cfg(windows)]
|
||||
use crate::utils::find_file;
|
||||
#[cfg(windows)]
|
||||
use std::path::Path;
|
||||
#[cfg(windows)]
|
||||
use normpath::PathExt as _;
|
||||
#[cfg(windows)]
|
||||
use same_file::is_same_file;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::AsRefStr;
|
||||
#[cfg(feature = "macchina")]
|
||||
use toml_edit::{value, DocumentMut, Item, Table};
|
||||
use tracing::debug;
|
||||
use unicode_segmentation::UnicodeSegmentation as _;
|
||||
use which::which;
|
||||
use crate::ascii::{RawAsciiArt, RecoloredAsciiArt};
|
||||
use crate::color_util::{printc, NeofetchAsciiIndexedColor, PresetIndexedColor};
|
||||
use crate::distros::Distro;
|
||||
use crate::types::{AnsiMode, Backend};
|
||||
use crate::utils::{find_in_path, get_cache_path, input, process_command_status};
|
||||
|
||||
pub const TEST_ASCII: &str = r####################"
|
||||
### |\___/| ###
|
||||
### ) ( ###
|
||||
## =\ /= ##
|
||||
#### )===( ####
|
||||
### / \ ###
|
||||
### | | ###
|
||||
## / {txt} \ ##
|
||||
## \ / ##
|
||||
_/\_\_ _/_/\_
|
||||
|##| ( ( |##|
|
||||
|##| ) ) |##|
|
||||
|##| (_( |##|
|
||||
"####################;
|
||||
|
||||
pub const NEOFETCH_COLOR_PATTERNS: [&str; 6] =
|
||||
["${c1}", "${c2}", "${c3}", "${c4}", "${c5}", "${c6}"];
|
||||
pub static NEOFETCH_COLORS_AC: OnceLock<AhoCorasick> = OnceLock::new();
|
||||
pub const NEOFETCH_SCRIPT: &str = include_str!("../../../neofetch");
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, AsRefStr, Deserialize, Serialize)]
|
||||
#[serde(tag = "mode")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum ColorAlignment {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Custom {
|
||||
#[serde(rename = "custom_colors")]
|
||||
#[serde(deserialize_with = "crate::utils::index_map_serde::deserialize")]
|
||||
colors: IndexMap<NeofetchAsciiIndexedColor, PresetIndexedColor>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Asks the user to provide an input among a list of options.
|
||||
pub fn literal_input<'a, S1, S2>(
|
||||
prompt: S1,
|
||||
options: &'a [S2],
|
||||
default: &str,
|
||||
show_options: bool,
|
||||
color_mode: AnsiMode,
|
||||
) -> Result<&'a str>
|
||||
where
|
||||
S1: AsRef<str>,
|
||||
S2: AsRef<str>,
|
||||
{
|
||||
let prompt = prompt.as_ref();
|
||||
|
||||
if show_options {
|
||||
let options_text = options
|
||||
.iter()
|
||||
.map(|o| {
|
||||
let o = o.as_ref();
|
||||
|
||||
if o == default {
|
||||
format!("&l&n{o}&L&N")
|
||||
} else {
|
||||
o.to_owned()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("|");
|
||||
printc(format!("{prompt} ({options_text})"), color_mode)
|
||||
.context("failed to print input prompt")?;
|
||||
} else {
|
||||
printc(format!("{prompt} (default: {default})"), color_mode)
|
||||
.context("failed to print input prompt")?;
|
||||
}
|
||||
|
||||
loop {
|
||||
let selection = input(Some("> ")).context("failed to read input")?;
|
||||
let selection = if selection.is_empty() {
|
||||
default.to_owned()
|
||||
} else {
|
||||
selection.to_lowercase()
|
||||
};
|
||||
|
||||
if let Some(selected) = find_selection(&selection, options) {
|
||||
writeln!(io::stdout()).context("failed to write to stdout")?;
|
||||
|
||||
return Ok(selected);
|
||||
} else {
|
||||
let options_text = options.iter().map(AsRef::as_ref).join("|");
|
||||
writeln!(
|
||||
io::stdout(),
|
||||
"Invalid selection! {selection} is not one of {options_text}"
|
||||
)
|
||||
.context("failed to write message to stdout")?;
|
||||
}
|
||||
}
|
||||
|
||||
fn find_selection<'a, S>(sel: &str, options: &'a [S]) -> Option<&'a str>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
if sel.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find exact match
|
||||
if let Some(selected) = options.iter().find(|&o| o.as_ref().to_lowercase() == sel) {
|
||||
return Some(selected.as_ref());
|
||||
}
|
||||
|
||||
// Find starting abbreviation
|
||||
if let Some(selected) = options
|
||||
.iter()
|
||||
.find(|&o| o.as_ref().to_lowercase().starts_with(sel))
|
||||
{
|
||||
return Some(selected.as_ref());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the PyPI pacakge path to the PATH environment variable (for this local process only).
|
||||
/// This is done so that `which` can find the commands inside the PyPI package.
|
||||
pub fn add_pkg_path() -> Result<()> {
|
||||
// Get PATH
|
||||
let pv = &env::var_os("PATH").context("`PATH` env var is not set or invalid")?;
|
||||
let mut path = env::split_paths(pv).collect::<Vec<_>>();
|
||||
let exe = env::current_exe().context("failed to get path of current running executable")?;
|
||||
let base = exe.parent().unwrap();
|
||||
|
||||
// Add from bin: ../git, ../fastfetch, ../scripts
|
||||
let to_add = ["git", "fastfetch", "scripts", "fastfetch/usr/bin"];
|
||||
if let Some(parent) = base.parent() {
|
||||
path.extend(to_add.iter().map(|d| parent.join(d)));
|
||||
}
|
||||
|
||||
// Add from cwd: ./hyfetch/git, ./hyfetch/fastfetch, ./hyfetch/scripts
|
||||
path.extend(to_add.iter().map(|d| PathBuf::from("hyfetch").join(d)));
|
||||
|
||||
// Set PATH
|
||||
env::set_var("PATH", env::join_paths(path).context("failed to join paths")?);
|
||||
debug!("Added PyPI package path to PATH, PATH={}", env::var("PATH")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the absolute path of the [neofetch] command.
|
||||
///
|
||||
/// [neofetch]: https://github.com/hykilpikonna/hyfetch#running-updated-original-neofetch
|
||||
pub fn neofetch_path() -> Result<PathBuf> {
|
||||
if let Ok(p) = which("neowofetch") {
|
||||
return Ok(p);
|
||||
}
|
||||
|
||||
// Instead of doing that, let's write the neofetch script to a temp file
|
||||
let f: PathBuf = get_cache_path().context("Failed to get cache path")?.join("nf_script.sh");
|
||||
let mut file = fs::File::create(&f).context("Failed to create neofetch script file")?;
|
||||
file.write_all(NEOFETCH_SCRIPT.as_bytes())
|
||||
.context("Failed to write neofetch script to file")?;
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
/// Gets the absolute path of the [macchina] command.
|
||||
///
|
||||
/// [macchina]: https://github.com/Macchina-CLI/macchina
|
||||
#[cfg(feature = "macchina")]
|
||||
pub fn macchina_path() -> Result<Option<PathBuf>> {
|
||||
let macchina_path = {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
find_in_path("macchina").context("failed to check existence of `macchina` in `PATH`")?
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
find_in_path("macchina.exe")
|
||||
.context("failed to check existence of `macchina.exe` in `PATH`")?
|
||||
}
|
||||
};
|
||||
|
||||
// Fall back to `macchina.exe` in directory of current executable
|
||||
#[cfg(windows)]
|
||||
let macchina_path = macchina_path.map_or_else(
|
||||
|| {
|
||||
let current_exe_path: PathBuf = env::current_exe()
|
||||
.and_then(|p| p.normalize().map(|p| p.into()))
|
||||
.context("failed to get path of current running executable")?;
|
||||
let current_exe_dir_path = current_exe_path
|
||||
.parent()
|
||||
.expect("parent should not be `None`");
|
||||
let macchina_path = current_exe_dir_path.join("macchina.exe");
|
||||
find_file(&macchina_path)
|
||||
.with_context(|| format!("failed to check existence of file {macchina_path:?}"))
|
||||
},
|
||||
|path| Ok(Some(path)),
|
||||
)?;
|
||||
|
||||
Ok(macchina_path)
|
||||
}
|
||||
|
||||
/// Gets the distro ascii of the current distro. Or if distro is specified, get
|
||||
/// the specific distro's ascii art instead.
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub fn get_distro_ascii<S>(distro: Option<S>, backend: Backend) -> Result<RawAsciiArt>
|
||||
where
|
||||
S: AsRef<str> + fmt::Debug,
|
||||
{
|
||||
let distro: Cow<_> = if let Some(distro) = distro.as_ref() {
|
||||
distro.as_ref().into()
|
||||
} else {
|
||||
get_distro_name(backend)
|
||||
.context("failed to get distro name")?
|
||||
.into()
|
||||
};
|
||||
debug!(%distro, "distro name");
|
||||
|
||||
// Try new codegen-based detection method
|
||||
if let Some(distro) = Distro::detect(&distro) {
|
||||
let asc = distro.ascii_art().to_owned();
|
||||
let fg = ascii_foreground(&distro);
|
||||
|
||||
return Ok(RawAsciiArt { asc, fg });
|
||||
}
|
||||
|
||||
debug!(%distro, "could not find a match for distro; falling back to neofetch");
|
||||
|
||||
// Old detection method that calls neofetch
|
||||
let asc = run_neofetch_command_piped(&["print_ascii", "--ascii_distro", distro.as_ref()])
|
||||
.context("failed to get ascii art from neofetch")?;
|
||||
|
||||
// Unescape backslashes here because backslashes are escaped in neofetch for
|
||||
// printf
|
||||
let asc = asc.replace(r"\\", r"\");
|
||||
|
||||
Ok(RawAsciiArt {
|
||||
asc,
|
||||
fg: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(asc), fields(asc.w = asc.w, asc.h = asc.h))]
|
||||
pub fn run(asc: RecoloredAsciiArt, backend: Backend, args: Option<&Vec<String>>) -> Result<()> {
|
||||
let asc = asc.lines.join("\n");
|
||||
|
||||
match backend {
|
||||
Backend::Neofetch => run_neofetch(asc, args).context("failed to run neofetch")?,
|
||||
Backend::Fastfetch => run_fastfetch(asc, args).context("failed to run fastfetch")?,
|
||||
#[cfg(feature = "macchina")]
|
||||
Backend::Macchina => run_macchina(asc, args).context("failed to run macchina")?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets distro ascii width and height, ignoring color code.
|
||||
pub fn ascii_size<S>(asc: S) -> Result<(u8, u8)>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let asc = asc.as_ref();
|
||||
|
||||
if asc.is_empty() {
|
||||
return Ok((0, 0));
|
||||
}
|
||||
|
||||
let asc = {
|
||||
let ac =
|
||||
NEOFETCH_COLORS_AC.get_or_init(|| AhoCorasick::new(NEOFETCH_COLOR_PATTERNS).unwrap());
|
||||
const N: usize = NEOFETCH_COLOR_PATTERNS.len();
|
||||
const REPLACEMENTS: [&str; N] = [""; N];
|
||||
ac.replace_all(asc, &REPLACEMENTS)
|
||||
};
|
||||
|
||||
if asc.is_empty() {
|
||||
return Ok((0, 0));
|
||||
}
|
||||
|
||||
let width = asc
|
||||
.lines()
|
||||
.map(|line| line.graphemes(true).count())
|
||||
.max()
|
||||
.expect("line iterator should not be empty");
|
||||
let width: u8 = width.try_into().with_context(|| {
|
||||
format!(
|
||||
"`asc` should not have more than {limit} characters per line",
|
||||
limit = u8::MAX
|
||||
)
|
||||
})?;
|
||||
let height = asc.lines().count();
|
||||
let height: u8 = height.try_into().with_context(|| {
|
||||
format!(
|
||||
"`asc` should not have more than {limit} lines",
|
||||
limit = u8::MAX
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((width, height))
|
||||
}
|
||||
|
||||
/// Gets the absolute path of the bash command.
|
||||
#[cfg(windows)]
|
||||
fn bash_path() -> Result<PathBuf> {
|
||||
// Find `bash.exe` in `PATH`, but exclude the known bad paths
|
||||
if let Some(bash_path) = find_in_path("bash.exe").context("bash.exe not found")? {
|
||||
// Check if it's not MSYS bash https://stackoverflow.com/a/58418686/1529493
|
||||
if !bash_path.ends_with(r"Git\usr\bin\bash.exe") {
|
||||
// Check if it's not WSL bash
|
||||
// See https://github.com/hykilpikonna/hyfetch/issues/233
|
||||
let windir = env::var_os("windir").context("`windir` environ not found")?;
|
||||
match is_same_file(&bash_path, Path::new(&windir).join(r"System32\bash.exe")) {
|
||||
Ok(false) => return Ok(bash_path),
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(bash_path),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(bash_path) = find_in_path("git.exe").context("failed to find `git.exe` in `PATH`")? {
|
||||
if bash_path.ends_with(r"Git\cmd\git.exe") {
|
||||
let pth = bash_path.parent().unwrap().parent().unwrap().join(r"bin\bash.exe");
|
||||
if pth.is_file() {
|
||||
return Ok(pth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("bash.exe not found"))
|
||||
}
|
||||
|
||||
/// Runs neofetch command, returning the piped stdout output.
|
||||
fn run_neofetch_command_piped<S>(args: &[S]) -> Result<String>
|
||||
where
|
||||
S: AsRef<OsStr> + fmt::Debug,
|
||||
{
|
||||
let mut command = make_neofetch_command(args)?;
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.context("failed to execute neofetch as child process")?;
|
||||
debug!(?output, "neofetch output");
|
||||
process_command_status(&output.status).context("neofetch command exited with error")?;
|
||||
|
||||
let out = String::from_utf8(output.stdout)
|
||||
.context("failed to process neofetch output as it contains invalid UTF-8")?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn make_neofetch_command<S>(args: &[S]) -> Result<Command>
|
||||
where
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
// Find neofetch script
|
||||
let neofetch_path = neofetch_path().context("neofetch command not found")?;
|
||||
|
||||
debug!(?neofetch_path, "neofetch path");
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let mut command = Command::new("bash");
|
||||
command.arg(neofetch_path);
|
||||
command.args(args);
|
||||
Ok(command)
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let bash_path = bash_path().context("failed to get bash path")?;
|
||||
let mut command = Command::new(bash_path);
|
||||
command.arg(neofetch_path);
|
||||
command.args(args);
|
||||
Ok(command)
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs fastfetch command, returning the piped stdout output.
|
||||
fn run_fastfetch_command_piped<S>(args: &[S]) -> Result<String>
|
||||
where
|
||||
S: AsRef<OsStr> + fmt::Debug,
|
||||
{
|
||||
let mut command = make_fastfetch_command(args)?;
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.context("failed to execute fastfetch as child process")?;
|
||||
debug!(?output, "fastfetch output");
|
||||
process_command_status(&output.status).context("fastfetch command exited with error")?;
|
||||
|
||||
let out = String::from_utf8(output.stdout)
|
||||
.context("failed to process fastfetch output as it contains invalid UTF-8")?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn fastfetch_path() -> Result<PathBuf> {
|
||||
which("fastfetch").context("fastfetch command not found")
|
||||
}
|
||||
|
||||
fn make_fastfetch_command<S>(args: &[S]) -> Result<Command>
|
||||
where
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
// Find fastfetch executable
|
||||
let ff_path = fastfetch_path()?;
|
||||
debug!(?ff_path, "fastfetch path");
|
||||
|
||||
let mut command = Command::new(ff_path);
|
||||
command.args(args);
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
/// Runs macchina command, returning the piped stdout output.
|
||||
#[cfg(feature = "macchina")]
|
||||
fn run_macchina_command_piped<S>(args: &[S]) -> Result<String>
|
||||
where
|
||||
S: AsRef<OsStr> + fmt::Debug,
|
||||
{
|
||||
let mut command = make_macchina_command(args)?;
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.context("failed to execute macchina as child process")?;
|
||||
debug!(?output, "macchina output");
|
||||
process_command_status(&output.status).context("macchina command exited with error")?;
|
||||
|
||||
let out = String::from_utf8(output.stdout)
|
||||
.context("failed to process macchina output as it contains invalid UTF-8")?
|
||||
.trim()
|
||||
.to_owned();
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[cfg(feature = "macchina")]
|
||||
fn make_macchina_command<S>(args: &[S]) -> Result<Command>
|
||||
where
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
// Find macchina executable
|
||||
let macchina_path = macchina_path()
|
||||
.context("failed to get macchina path")?
|
||||
.context("macchina command not found")?;
|
||||
|
||||
debug!(?macchina_path, "macchina path");
|
||||
|
||||
let mut command = Command::new(macchina_path);
|
||||
command.args(args);
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub(crate) fn get_distro_name(backend: Backend) -> Result<String> {
|
||||
match backend {
|
||||
Backend::Neofetch => run_neofetch_command_piped(&["ascii_distro_name"])
|
||||
.context("failed to get distro name from neofetch"),
|
||||
Backend::Fastfetch => run_fastfetch_command_piped(&[
|
||||
"--logo",
|
||||
"none",
|
||||
"-s",
|
||||
"OS",
|
||||
"--disable-linewrap",
|
||||
"--os-key",
|
||||
" ",
|
||||
])
|
||||
.context("failed to get distro name from fastfetch"),
|
||||
#[cfg(feature = "macchina")]
|
||||
Backend::Macchina => {
|
||||
// Write ascii art to temp file
|
||||
let asc_file_path = {
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.suffix("ascii.txt")
|
||||
.tempfile()
|
||||
.context("failed to create temp file for ascii art")?;
|
||||
temp_file
|
||||
.write_all(b"\t\n\t\n")
|
||||
.context("failed to write ascii art to temp file")?;
|
||||
temp_file.into_temp_path()
|
||||
};
|
||||
|
||||
// Write macchina theme to temp file
|
||||
let theme_file_path = {
|
||||
let project_dirs = directories::ProjectDirs::from("", "", "macchina")
|
||||
.context("failed to get base dirs")?;
|
||||
let themes_path = project_dirs.config_dir().join("themes");
|
||||
fs::create_dir_all(&themes_path).with_context(|| {
|
||||
format!("failed to create macchina themes dir {themes_path:?}")
|
||||
})?;
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.suffix("theme.toml")
|
||||
.tempfile_in(themes_path)
|
||||
.context("failed to create temp file for macchina theme")?;
|
||||
let theme_doc = {
|
||||
let mut doc = DocumentMut::new();
|
||||
doc["spacing"] = value(0);
|
||||
doc["padding"] = value(0);
|
||||
// See https://github.com/Macchina-CLI/macchina/issues/319
|
||||
// doc["hide_ascii"] = value(true);
|
||||
doc["separator"] = value("");
|
||||
doc["custom_ascii"] = Item::Table(Table::from_iter([(
|
||||
"path",
|
||||
&*asc_file_path.to_string_lossy(),
|
||||
)]));
|
||||
doc["keys"] = Item::Table(Table::from_iter([("os", ""), ("distro", "")]));
|
||||
doc
|
||||
};
|
||||
debug!(%theme_doc, "macchina theme");
|
||||
temp_file
|
||||
.write_all(theme_doc.to_string().as_bytes())
|
||||
.context("failed to write macchina theme to temp file")?;
|
||||
temp_file.into_temp_path()
|
||||
};
|
||||
|
||||
let args: [&OsStr; 4] = [
|
||||
"--show".as_ref(),
|
||||
if cfg!(target_os = "linux") {
|
||||
"distribution"
|
||||
} else {
|
||||
"operating-system"
|
||||
}
|
||||
.as_ref(),
|
||||
"--theme".as_ref(),
|
||||
theme_file_path
|
||||
.file_stem()
|
||||
.expect("file name should not be `None`"),
|
||||
];
|
||||
run_macchina_command_piped(&args[..])
|
||||
.map(|s| {
|
||||
anstream::adapter::strip_str(&s)
|
||||
.to_string()
|
||||
.trim()
|
||||
.to_owned()
|
||||
})
|
||||
.context("failed to get distro name from macchina")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs neofetch with custom ascii art.
|
||||
#[tracing::instrument(level = "debug", skip(asc))]
|
||||
fn run_neofetch(asc: String, args: Option<&Vec<String>>) -> Result<()> {
|
||||
// Escape backslashes here because backslashes are escaped in neofetch for
|
||||
// printf
|
||||
let asc = asc.replace('\\', r"\\");
|
||||
|
||||
// Write ascii art to temp file
|
||||
let asc_file_path = {
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.suffix("ascii.txt")
|
||||
.tempfile()
|
||||
.context("failed to create temp file for ascii art")?;
|
||||
temp_file
|
||||
.write_all(asc.as_bytes())
|
||||
.context("failed to write ascii art to temp file")?;
|
||||
temp_file.into_temp_path()
|
||||
};
|
||||
|
||||
// Call neofetch
|
||||
let args = {
|
||||
let mut v: Vec<Cow<OsStr>> = vec![
|
||||
OsStr::new("--ascii").into(),
|
||||
OsStr::new("--source").into(),
|
||||
OsStr::new(&asc_file_path).into(),
|
||||
OsStr::new("--ascii_colors").into(),
|
||||
];
|
||||
if let Some(args) = args {
|
||||
v.extend(args.iter().map(|arg| OsStr::new(arg).into()));
|
||||
}
|
||||
v
|
||||
};
|
||||
let mut command = make_neofetch_command(&args[..])?;
|
||||
|
||||
debug!(?command, "neofetch command");
|
||||
|
||||
let status = command
|
||||
.status()
|
||||
.context("failed to execute neofetch command as child process")?;
|
||||
process_command_status(&status).context("neofetch command exited with error")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs fastfetch with custom ascii art.
|
||||
#[tracing::instrument(level = "debug", skip(asc))]
|
||||
fn run_fastfetch(asc: String, args: Option<&Vec<String>>) -> Result<()> {
|
||||
// Write ascii art to temp file
|
||||
let asc_file_path = {
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.suffix("ascii.txt")
|
||||
.tempfile()
|
||||
.context("failed to create temp file for ascii art")?;
|
||||
temp_file
|
||||
.write_all(asc.as_bytes())
|
||||
.context("failed to write ascii art to temp file")?;
|
||||
temp_file.into_temp_path()
|
||||
};
|
||||
|
||||
// Call fastfetch
|
||||
let args = {
|
||||
let mut v: Vec<Cow<OsStr>> = vec![
|
||||
OsStr::new("--file-raw").into(),
|
||||
OsStr::new(&asc_file_path).into(),
|
||||
];
|
||||
if let Some(args) = args {
|
||||
v.extend(args.iter().map(|arg| OsStr::new(arg).into()));
|
||||
}
|
||||
v
|
||||
};
|
||||
let mut command = make_fastfetch_command(&args[..])?;
|
||||
|
||||
debug!(?command, "fastfetch command");
|
||||
|
||||
let status = command
|
||||
.status()
|
||||
.context("failed to execute fastfetch command as child process")?;
|
||||
process_command_status(&status).context("fastfetch command exited with error")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs macchina with custom ascii art.
|
||||
#[cfg(feature = "macchina")]
|
||||
#[tracing::instrument(level = "debug", skip(asc))]
|
||||
fn run_macchina(asc: String, args: Option<&Vec<String>>) -> Result<()> {
|
||||
// Write ascii art to temp file
|
||||
let asc_file_path = {
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.suffix("ascii.txt")
|
||||
.tempfile()
|
||||
.context("failed to create temp file for ascii art")?;
|
||||
temp_file
|
||||
.write_all(asc.as_bytes())
|
||||
.context("failed to write ascii art to temp file")?;
|
||||
temp_file.into_temp_path()
|
||||
};
|
||||
|
||||
// Write macchina theme to temp file
|
||||
let theme_file_path = {
|
||||
let project_dirs = directories::ProjectDirs::from("", "", "macchina")
|
||||
.context("failed to get base dirs")?;
|
||||
let themes_path = project_dirs.config_dir().join("themes");
|
||||
fs::create_dir_all(&themes_path)
|
||||
.with_context(|| format!("failed to create macchina themes dir {themes_path:?}"))?;
|
||||
let mut temp_file = tempfile::Builder::new()
|
||||
.suffix("theme.toml")
|
||||
.tempfile_in(themes_path)
|
||||
.context("failed to create temp file for macchina theme")?;
|
||||
let theme_doc = {
|
||||
let mut doc = DocumentMut::new();
|
||||
doc["custom_ascii"] = Item::Table(Table::from_iter([(
|
||||
"path",
|
||||
&*asc_file_path.to_string_lossy(),
|
||||
)]));
|
||||
doc
|
||||
};
|
||||
debug!(%theme_doc, "macchina theme");
|
||||
temp_file
|
||||
.write_all(theme_doc.to_string().as_bytes())
|
||||
.context("failed to write macchina theme to temp file")?;
|
||||
temp_file.into_temp_path()
|
||||
};
|
||||
|
||||
let args = {
|
||||
let mut v: Vec<Cow<OsStr>> = vec![
|
||||
OsStr::new("--theme").into(),
|
||||
theme_file_path
|
||||
.file_stem()
|
||||
.expect("file name should not be `None`")
|
||||
.into(),
|
||||
];
|
||||
if let Some(args) = args {
|
||||
v.extend(args.iter().map(|arg| OsStr::new(arg).into()));
|
||||
}
|
||||
v
|
||||
};
|
||||
let mut command = make_macchina_command(&args[..])?;
|
||||
|
||||
debug!(?command, "macchina command");
|
||||
|
||||
let status = command
|
||||
.status()
|
||||
.context("failed to execute macchina command as child process")?;
|
||||
process_command_status(&status).context("macchina command exited with error")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the color indices that should be considered as foreground, for a
|
||||
/// particular distro's ascii art.
|
||||
fn ascii_foreground(distro: &Distro) -> Vec<NeofetchAsciiIndexedColor> {
|
||||
let fg: Vec<u8> = match distro {
|
||||
Distro::Anarchy => vec![2],
|
||||
Distro::Android => vec![2],
|
||||
Distro::Antergos => vec![1],
|
||||
Distro::ArchStrike => vec![2],
|
||||
Distro::Arkane => vec![1],
|
||||
Distro::Asahi => vec![5],
|
||||
Distro::Astra_Linux => vec![2],
|
||||
Distro::BlackArch => vec![3],
|
||||
Distro::CelOS => vec![3],
|
||||
Distro::Chapeau => vec![2],
|
||||
Distro::Chrom => vec![5],
|
||||
Distro::Clear_Linux_OS => vec![2],
|
||||
Distro::Container_Linux_by_CoreOS => vec![3],
|
||||
Distro::CRUX => vec![3],
|
||||
Distro::EuroLinux => vec![2],
|
||||
Distro::eweOS => vec![3],
|
||||
Distro::Fedora => vec![2],
|
||||
Distro::Fedora_Sericea => vec![2],
|
||||
Distro::Fedora_Silverblue => vec![2],
|
||||
Distro::GalliumOS => vec![2],
|
||||
Distro::Gentoo => vec![1],
|
||||
Distro::HarDClanZ => vec![2],
|
||||
Distro::Kibojoe => vec![3],
|
||||
Distro::KrassOS => vec![2],
|
||||
Distro::Kubuntu => vec![2],
|
||||
Distro::Linux => vec![1],
|
||||
Distro::LinuxFromScratch => vec![1, 3],
|
||||
Distro::Lubuntu => vec![2],
|
||||
Distro::openEuler => vec![2],
|
||||
Distro::orchid => vec![1],
|
||||
Distro::Panwah => vec![1],
|
||||
Distro::Peppermint => vec![2],
|
||||
Distro::PNM_Linux => vec![2],
|
||||
Distro::Pop__OS => vec![2],
|
||||
Distro::Reborn_OS => vec![1],
|
||||
Distro::SalentOS => vec![4],
|
||||
Distro::Septor => vec![2],
|
||||
Distro::Ubuntu_Cinnamon => vec![2],
|
||||
Distro::Ubuntu_Kylin => vec![2],
|
||||
Distro::Ubuntu_MATE => vec![2],
|
||||
Distro::Ubuntu_old => vec![2],
|
||||
Distro::Ubuntu_Studio => vec![2],
|
||||
Distro::Ubuntu_Sway => vec![2],
|
||||
Distro::Ultramarine_Linux => vec![2],
|
||||
Distro::Univention => vec![2],
|
||||
Distro::uwuntu => vec![2],
|
||||
Distro::Vanilla => vec![2],
|
||||
Distro::VNux => vec![3, 5],
|
||||
Distro::Void => vec![2],
|
||||
Distro::Xray_OS => vec![2, 3],
|
||||
Distro::Xubuntu => vec![2],
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
fg.into_iter()
|
||||
.map(|fore| {
|
||||
fore.try_into()
|
||||
.expect("`fore` should be a valid neofetch color index")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,846 @@
|
||||
use std::iter;
|
||||
use std::num::{NonZeroU8, NonZeroUsize};
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use indexmap::IndexSet;
|
||||
use palette::num::ClampAssign as _;
|
||||
use palette::{IntoColorMut as _, LinSrgb, Okhsl, Srgb};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{AsRefStr, EnumCount, EnumString, VariantArray, VariantNames};
|
||||
use tracing::debug;
|
||||
use unicode_segmentation::UnicodeSegmentation as _;
|
||||
|
||||
use crate::color_util::{ForegroundBackground, Lightness, ToAnsiString as _};
|
||||
use crate::types::{AnsiMode, TerminalTheme};
|
||||
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
Debug,
|
||||
AsRefStr,
|
||||
Deserialize,
|
||||
EnumCount,
|
||||
EnumString,
|
||||
Serialize,
|
||||
VariantArray,
|
||||
VariantNames,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Preset {
|
||||
Rainbow,
|
||||
|
||||
Transgender,
|
||||
|
||||
Nonbinary,
|
||||
|
||||
Xenogender,
|
||||
|
||||
Agender,
|
||||
|
||||
Queer,
|
||||
|
||||
Genderfluid,
|
||||
|
||||
Bisexual,
|
||||
|
||||
Pansexual,
|
||||
|
||||
Polysexual,
|
||||
|
||||
Omnisexual,
|
||||
|
||||
Omniromantic,
|
||||
|
||||
GayMen,
|
||||
|
||||
Lesbian,
|
||||
|
||||
Abrosexual,
|
||||
|
||||
Asexual,
|
||||
|
||||
Aromantic,
|
||||
|
||||
Aroace1,
|
||||
|
||||
Aroace2,
|
||||
|
||||
Aroace3,
|
||||
|
||||
Greysexual,
|
||||
|
||||
Autosexual,
|
||||
|
||||
Intergender,
|
||||
|
||||
Greygender,
|
||||
|
||||
Akiosexual,
|
||||
|
||||
Bigender,
|
||||
|
||||
Demigender,
|
||||
|
||||
Demiboy,
|
||||
|
||||
Demigirl,
|
||||
|
||||
Transmasculine,
|
||||
|
||||
Transfeminine,
|
||||
|
||||
Genderfaun,
|
||||
|
||||
Demifaun,
|
||||
|
||||
Genderfae,
|
||||
|
||||
Demifae,
|
||||
|
||||
Neutrois,
|
||||
|
||||
Biromantic1,
|
||||
|
||||
Autoromantic,
|
||||
|
||||
Boyflux2,
|
||||
|
||||
Girlflux,
|
||||
|
||||
Genderflux,
|
||||
|
||||
Finsexual,
|
||||
|
||||
Unlabeled1,
|
||||
|
||||
Unlabeled2,
|
||||
|
||||
Pangender,
|
||||
|
||||
/// High-contrast version of pangender flag
|
||||
#[serde(rename = "pangender.contrast")]
|
||||
#[strum(serialize = "pangender.contrast")]
|
||||
PangenderContrast,
|
||||
|
||||
#[serde(rename = "gendernonconforming1")]
|
||||
#[strum(serialize = "gendernonconforming1")]
|
||||
GenderNonconforming1,
|
||||
|
||||
#[serde(rename = "gendernonconforming2")]
|
||||
#[strum(serialize = "gendernonconforming2")]
|
||||
GenderNonconforming2,
|
||||
|
||||
Femboy,
|
||||
|
||||
Tomboy,
|
||||
|
||||
Gynesexual,
|
||||
|
||||
Androsexual,
|
||||
|
||||
Gendervoid,
|
||||
|
||||
Voidgirl,
|
||||
|
||||
Voidboy,
|
||||
|
||||
NonhumanUnity,
|
||||
|
||||
/// For all the dogs
|
||||
Caninekin,
|
||||
|
||||
Plural,
|
||||
|
||||
Fraysexual,
|
||||
|
||||
Bear,
|
||||
|
||||
Butch,
|
||||
|
||||
Leather,
|
||||
|
||||
Otter,
|
||||
|
||||
Twink,
|
||||
|
||||
Kenochoric,
|
||||
|
||||
Veldian,
|
||||
|
||||
Solian,
|
||||
|
||||
Lunian,
|
||||
|
||||
Polyam,
|
||||
|
||||
Sapphic,
|
||||
|
||||
Androgyne,
|
||||
|
||||
Interprogress,
|
||||
|
||||
Progress,
|
||||
|
||||
Intersex,
|
||||
|
||||
OldPolyam,
|
||||
|
||||
EqualRights,
|
||||
|
||||
Drag,
|
||||
|
||||
Pronounfluid,
|
||||
|
||||
Pronounflux,
|
||||
|
||||
Exipronoun,
|
||||
|
||||
Neopronoun,
|
||||
|
||||
Neofluid,
|
||||
|
||||
Genderqueer,
|
||||
|
||||
/// Meme flag
|
||||
Beiyang,
|
||||
|
||||
/// Meme flag
|
||||
Burger,
|
||||
|
||||
/// Meme flag
|
||||
#[serde(rename = "throatlozenges")]
|
||||
#[strum(serialize = "throatlozenges")]
|
||||
ThroatLozenges,
|
||||
|
||||
/// Colors from Gilbert Baker's original 1978 flag design
|
||||
Baker,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct ColorProfile {
|
||||
pub colors: Vec<Srgb<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum AssignLightness {
|
||||
Replace(Lightness),
|
||||
ClampMax(Lightness),
|
||||
ClampMin(Lightness),
|
||||
}
|
||||
|
||||
impl Preset {
|
||||
pub fn color_profile(&self) -> ColorProfile {
|
||||
(match self {
|
||||
Self::Rainbow => ColorProfile::from_hex_colors(vec![
|
||||
"#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088",
|
||||
]),
|
||||
|
||||
Self::Transgender => ColorProfile::from_hex_colors(vec![
|
||||
"#55CDFD", "#F6AAB7", "#FFFFFF", "#F6AAB7", "#55CDFD",
|
||||
]),
|
||||
|
||||
Self::Nonbinary => {
|
||||
ColorProfile::from_hex_colors(vec!["#FCF431", "#FCFCFC", "#9D59D2", "#282828"])
|
||||
},
|
||||
|
||||
// sourced from https://commons.wikimedia.org/wiki/File:Xenogender_pride_flag.svg
|
||||
Self::Xenogender => ColorProfile::from_hex_colors(vec![
|
||||
"#FF6692", "#FF9A98", "#FFB883", "#FBFFA8", "#85BCFF", "#9D85FF", "#A510FF",
|
||||
]),
|
||||
|
||||
Self::Agender => ColorProfile::from_hex_colors(vec![
|
||||
"#000000", "#BABABA", "#FFFFFF", "#BAF484", "#FFFFFF", "#BABABA", "#000000",
|
||||
]),
|
||||
|
||||
Self::Queer => ColorProfile::from_hex_colors(vec!["#B57FDD", "#FFFFFF", "#49821E"]),
|
||||
|
||||
Self::Genderfluid => ColorProfile::from_hex_colors(vec![
|
||||
"#FE76A2", "#FFFFFF", "#BF12D7", "#000000", "#303CBE",
|
||||
]),
|
||||
|
||||
Self::Bisexual => ColorProfile::from_hex_colors(vec!["#D60270", "#9B4F96", "#0038A8"]),
|
||||
|
||||
Self::Pansexual => ColorProfile::from_hex_colors(vec!["#FF1C8D", "#FFD700", "#1AB3FF"]),
|
||||
|
||||
Self::Polysexual => {
|
||||
ColorProfile::from_hex_colors(vec!["#F714BA", "#01D66A", "#1594F6"])
|
||||
},
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/omnisexual
|
||||
Self::Omnisexual => ColorProfile::from_hex_colors(vec![
|
||||
"#FE9ACE", "#FF53BF", "#200044", "#6760FE", "#8EA6FF",
|
||||
]),
|
||||
|
||||
Self::Omniromantic => ColorProfile::from_hex_colors(vec![
|
||||
"#FEC8E4", "#FDA1DB", "#89739A", "#ABA7FE", "#BFCEFF",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/gay-men
|
||||
Self::GayMen => ColorProfile::from_hex_colors(vec![
|
||||
"#078D70", "#98E8C1", "#FFFFFF", "#7BADE2", "#3D1A78",
|
||||
]),
|
||||
|
||||
Self::Lesbian => ColorProfile::from_hex_colors(vec![
|
||||
"#D62800", "#FF9B56", "#FFFFFF", "#D462A6", "#A40062",
|
||||
]),
|
||||
|
||||
// used colorpicker to source from https://fyeahaltpride.tumblr.com/post/151704251345/could-you-guys-possibly-make-an-abrosexual-pride
|
||||
Self::Abrosexual => ColorProfile::from_hex_colors(vec![
|
||||
"#46D294", "#A3E9CA", "#FFFFFF", "#F78BB3", "#EE1766",
|
||||
]),
|
||||
|
||||
Self::Asexual => {
|
||||
ColorProfile::from_hex_colors(vec!["#000000", "#A4A4A4", "#FFFFFF", "#810081"])
|
||||
},
|
||||
|
||||
Self::Aromantic => ColorProfile::from_hex_colors(vec![
|
||||
"#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000",
|
||||
]),
|
||||
|
||||
// sourced from https://flag.library.lgbt/flags/aroace/
|
||||
Self::Aroace1 => ColorProfile::from_hex_colors(vec![
|
||||
"#E28C00", "#ECCD00", "#FFFFFF", "#62AEDC", "#203856",
|
||||
]),
|
||||
|
||||
// sourced from https://flag.library.lgbt/flags/aroace/
|
||||
Self::Aroace2 => ColorProfile::from_hex_colors(vec![
|
||||
"#000000", "#810081", "#A4A4A4", "#FFFFFF", "#A8D47A", "#3BA740",
|
||||
]),
|
||||
|
||||
// sourced from https://flag.library.lgbt/flags/aroace/
|
||||
Self::Aroace3 => ColorProfile::from_hex_colors(vec![
|
||||
"#3BA740", "#A8D47A", "#FFFFFF", "#ABABAB", "#000000", "#A4A4A4", "#FFFFFF",
|
||||
"#810081",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/greysexual
|
||||
Self::Greysexual => ColorProfile::from_hex_colors(vec![
|
||||
"#740194", "#AEB1AA", "#FFFFFF", "#AEB1AA", "#740194",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/autosexual
|
||||
Self::Autosexual => ColorProfile::from_hex_colors(vec!["#99D9EA", "#7F7F7F"]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/intergender
|
||||
Self::Intergender => {
|
||||
ColorProfile::from_hex_colors(vec!["#900DC2", "#FFE54F", "#900DC2"])
|
||||
.and_then(|c| c.with_weights(vec![2, 1, 2]))
|
||||
},
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/greygender
|
||||
Self::Greygender => ColorProfile::from_hex_colors(vec![
|
||||
"#B3B3B3", "#FFFFFF", "#062383", "#FFFFFF", "#535353",
|
||||
])
|
||||
.and_then(|c| c.with_weights(vec![2, 1, 2, 1, 2])),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/akiosexual
|
||||
Self::Akiosexual => ColorProfile::from_hex_colors(vec![
|
||||
"#F9485E", "#FEA06A", "#FEF44C", "#FFFFFF", "#000000",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/bigender
|
||||
Self::Bigender => ColorProfile::from_hex_colors(vec![
|
||||
"#C479A2", "#EDA5CD", "#D6C7E8", "#FFFFFF", "#D6C7E8", "#9AC7E8", "#6D82D1",
|
||||
]),
|
||||
|
||||
// yellow sourced from https://lgbtqia.fandom.com/f/p/4400000000000041031
|
||||
// other colors sourced from demiboy and demigirl flags
|
||||
Self::Demigender => ColorProfile::from_hex_colors(vec![
|
||||
"#7F7F7F", "#C4C4C4", "#FBFF75", "#FFFFFF", "#FBFF75", "#C4C4C4", "#7F7F7F",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/demiboy
|
||||
Self::Demiboy => ColorProfile::from_hex_colors(vec![
|
||||
"#7F7F7F", "#C4C4C4", "#9DD7EA", "#FFFFFF", "#9DD7EA", "#C4C4C4", "#7F7F7F",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/demigirl
|
||||
Self::Demigirl => ColorProfile::from_hex_colors(vec![
|
||||
"#7F7F7F", "#C4C4C4", "#FDADC8", "#FFFFFF", "#FDADC8", "#C4C4C4", "#7F7F7F",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/transmasculine
|
||||
Self::Transmasculine => ColorProfile::from_hex_colors(vec![
|
||||
"#FF8ABD", "#CDF5FE", "#9AEBFF", "#74DFFF", "#9AEBFF", "#CDF5FE", "#FF8ABD",
|
||||
]),
|
||||
|
||||
// used colorpicker to source from https://www.deviantart.com/pride-flags/art/Trans-Woman-Transfeminine-1-543925985
|
||||
// linked from https://gender.fandom.com/wiki/Transfeminine
|
||||
Self::Transfeminine => ColorProfile::from_hex_colors(vec![
|
||||
"#73DEFF", "#FFE2EE", "#FFB5D6", "#FF8DC0", "#FFB5D6", "#FFE2EE", "#73DEFF",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/genderfaun
|
||||
Self::Genderfaun => ColorProfile::from_hex_colors(vec![
|
||||
"#FCD689", "#FFF09B", "#FAF9CD", "#FFFFFF", "#8EDED9", "#8CACDE", "#9782EC",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/demifaun
|
||||
Self::Demifaun => ColorProfile::from_hex_colors(vec![
|
||||
"#7F7F7F", "#C6C6C6", "#FCC688", "#FFF19C", "#FFFFFF", "#8DE0D5", "#9682EC",
|
||||
"#C6C6C6", "#7F7F7F",
|
||||
])
|
||||
.and_then(|c| c.with_weights(vec![2, 2, 1, 1, 1, 1, 1, 2, 2])),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/genderfae
|
||||
Self::Genderfae => ColorProfile::from_hex_colors(vec![
|
||||
"#97C3A5", "#C3DEAE", "#F9FACD", "#FFFFFF", "#FCA2C4", "#DB8AE4", "#A97EDD",
|
||||
]),
|
||||
|
||||
// used colorpicker to source form https://www.deviantart.com/pride-flags/art/Demifae-870194777
|
||||
Self::Demifae => ColorProfile::from_hex_colors(vec![
|
||||
"#7F7F7F", "#C5C5C5", "#97C3A4", "#C4DEAE", "#FFFFFF", "#FCA2C5", "#AB7EDF",
|
||||
"#C5C5C5", "#7F7F7F",
|
||||
])
|
||||
.and_then(|c| c.with_weights(vec![2, 2, 1, 1, 1, 1, 1, 2, 2])),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/neutrois
|
||||
Self::Neutrois => ColorProfile::from_hex_colors(vec!["#FFFFFF", "#1F9F00", "#000000"]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/biromantic-alternate-2
|
||||
Self::Biromantic1 => ColorProfile::from_hex_colors(vec![
|
||||
"#8869A5", "#D8A7D8", "#FFFFFF", "#FDB18D", "#151638",
|
||||
]),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/autoromantic
|
||||
Self::Autoromantic => ColorProfile::from_hex_colors(
|
||||
// symbol interpreted
|
||||
vec!["#99D9EA", "#3DA542", "#7F7F7F"],
|
||||
)
|
||||
.and_then(|c| c.with_weights(vec![2, 1, 2])),
|
||||
|
||||
// sourced from https://www.flagcolorcodes.com/boyflux-alternate-2
|
||||
Self::Boyflux2 => ColorProfile::from_hex_colors(vec![
|
||||
"#E48AE4", "#9A81B4", "#55BFAB", "#FFFFFF", "#A8A8A8", "#81D5EF", "#69ABE5",
|
||||
"#5276D4",
|
||||
])
|
||||
.and_then(|c| c.with_weights(vec![1, 1, 1, 1, 1, 5, 5, 5])),
|
||||
|
||||
// sourced from https://commons.wikimedia.org/wiki/File:Girlflux_Pride_Flag.jpg
|
||||
Self::Girlflux => ColorProfile::from_hex_colors(vec![
|
||||
"f9e6d7", "f2526c", "bf0311", "e9c587", "bf0311", "f2526c", "f9e6d7",
|
||||
]),
|
||||
|
||||
// sourced from https://www.deviantart.com/pride-flags/art/Genderflux-1-543925589
|
||||
Self::Genderflux => ColorProfile::from_hex_colors(vec![
|
||||
"f47694", "f2a2b9", "cecece", "7ce0f7", "3ecdf9", "fff48d",
|
||||
]),
|
||||
|
||||
// sourced from https://lgbtqia.wiki/wiki/Finsexual
|
||||
Self::Finsexual => ColorProfile::from_hex_colors(vec![
|
||||
"#B18EDF", "#D7B1E2", "#F7CDE9", "#F39FCE", "#EA7BB3",
|
||||
]),
|
||||
|
||||
// sourced from https://web.archive.org/web/20221002181913/https://unlabeledinfo.carrd.co/#flags
|
||||
Self::Unlabeled1 => {
|
||||
ColorProfile::from_hex_colors(vec!["#EAF8E4", "#FDFDFB", "#E1EFF7", "#F4E2C4"])
|
||||
},
|
||||
|
||||
// sourced from https://web.archive.org/web/20221002181913/https://unlabeledinfo.carrd.co/#flags
|
||||
Self::Unlabeled2 => ColorProfile::from_hex_colors(vec![
|
||||
"#250548", "#FFFFFF", "#F7DCDA", "#EC9BEE", "#9541FA", "#7D2557",
|
||||
]),
|
||||
|
||||
Self::Pangender => ColorProfile::from_hex_colors(vec![
|
||||
"#FFF798", "#FEDDCD", "#FFEBFB", "#FFFFFF", "#FFEBFB", "#FEDDCD", "#FFF798",
|
||||
]),
|
||||
|
||||
// high-contrast version of pangender flag
|
||||
Self::PangenderContrast => ColorProfile::from_hex_colors(vec![
|
||||
"#ffe87f", "#fcbaa6", "#fbc9f3", "#FFFFFF", "#fbc9f3", "#fcbaa6", "#ffe87f",
|
||||
]),
|
||||
|
||||
Self::GenderNonconforming1 => ColorProfile::from_hex_colors(vec![
|
||||
"#50284d", "#96467b", "#5c96f7", "#ffe6f7", "#5c96f7", "#96467b", "#50284d",
|
||||
])
|
||||
.and_then(|c| c.with_weights(vec![4, 1, 1, 1, 1, 1, 4])),
|
||||
|
||||
Self::GenderNonconforming2 => ColorProfile::from_hex_colors(vec![
|
||||
"#50284d", "#96467b", "#5c96f7", "#ffe6f7", "#5c96f7", "#96467b", "#50284d",
|
||||
]),
|
||||
|
||||
Self::Femboy => ColorProfile::from_hex_colors(vec![
|
||||
"#d260a5", "#e4afcd", "#fefefe", "#57cef8", "#fefefe", "#e4afcd", "#d260a5",
|
||||
]),
|
||||
|
||||
Self::Tomboy => ColorProfile::from_hex_colors(vec![
|
||||
"#2f3fb9", "#613a03", "#fefefe", "#f1a9b7", "#fefefe", "#613a03", "#2f3fb9",
|
||||
]),
|
||||
|
||||
// sourced from https://lgbtqia.fandom.com/wiki/Gynesexual
|
||||
Self::Gynesexual => {
|
||||
ColorProfile::from_hex_colors(vec!["#F4A9B7", "#903F2B", "#5B953B"])
|
||||
},
|
||||
|
||||
// sourced from https://lgbtqia.fandom.com/wiki/Androsexual
|
||||
Self::Androsexual => {
|
||||
ColorProfile::from_hex_colors(vec!["#01CCFF", "#603524", "#B799DE"])
|
||||
},
|
||||
|
||||
// sourced from: https://gender.fandom.com/wiki/Gendervoid
|
||||
Self::Gendervoid => ColorProfile::from_hex_colors(vec![
|
||||
"#081149", "#4B484B", "#000000", "#4B484B", "#081149",
|
||||
]),
|
||||
|
||||
// sourced from: https://gender.fandom.com/wiki/Gendervoid
|
||||
Self::Voidgirl => ColorProfile::from_hex_colors(vec![
|
||||
"#180827", "#7A5A8B", "#E09BED", "#7A5A8B", "#180827",
|
||||
]),
|
||||
|
||||
// sourced from: https://gender.fandom.com/wiki/Gendervoid
|
||||
Self::Voidboy => ColorProfile::from_hex_colors(vec![
|
||||
"#0B130C", "#547655", "#66B969", "#547655", "#0B130C",
|
||||
]),
|
||||
|
||||
// used https://twitter.com/foxbrained/status/1667621855518236674/photo/1 as source and colorpicked
|
||||
Self::NonhumanUnity => {
|
||||
ColorProfile::from_hex_colors(vec!["#177B49", "#FFFFFF", "#593C90"])
|
||||
},
|
||||
|
||||
// used https://www.tumblr.com/zombpawcoins/745062851267493888/caninekin-canine-therian-flag
|
||||
Self::Caninekin => ColorProfile::from_hex_colors(vec![
|
||||
"#2d2822", "#543d25", "#9c754d", "#e8dac2", "#cfad8c", "#b77b55", "#954e31",
|
||||
]),
|
||||
|
||||
// used https://pluralpedia.org/w/Plurality#/media/File:Plural-Flag-1.jpg as source and colorpicked
|
||||
Self::Plural => ColorProfile::from_hex_colors(vec![
|
||||
"#2D0625", "#543475", "#7675C3", "#89C7B0", "#F3EDBD",
|
||||
]),
|
||||
|
||||
// sampled from https://es.m.wikipedia.org/wiki/Archivo:Fraysexual_flag.jpg
|
||||
Self::Fraysexual => {
|
||||
ColorProfile::from_hex_colors(vec!["#226CB5", "#94E7DD", "#FFFFFF", "#636363"])
|
||||
},
|
||||
|
||||
// sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg
|
||||
Self::Bear => ColorProfile::from_hex_colors(vec![
|
||||
"#623804", "#D56300", "#FEDD63", "#FEE6B8", "#FFFFFF", "#555555",
|
||||
]),
|
||||
|
||||
// colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png
|
||||
Self::Butch => ColorProfile::from_hex_colors(vec![
|
||||
"#D72800", "#F17623", "#FF9C56", "#FFFDF6", "#FFCE89", "#FEAF02", "#A37000",
|
||||
]),
|
||||
|
||||
// colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg
|
||||
Self::Leather => ColorProfile::from_hex_colors(vec![
|
||||
"#000000", "#252580", "#000000", "#252580", "#FFFFFF", "#252580", "#000000",
|
||||
"#252580", "#000000",
|
||||
]),
|
||||
|
||||
// colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg
|
||||
Self::Otter => ColorProfile::from_hex_colors(vec![
|
||||
"#263881", "#5C9DC9", "#FFFFFF", "#3A291D", "#5C9DC9", "#263881",
|
||||
]),
|
||||
|
||||
// colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg
|
||||
Self::Twink => ColorProfile::from_hex_colors(vec!["#FFB2FF", "#FFFFFF", "#FFFF81"]),
|
||||
|
||||
Self::Kenochoric => {
|
||||
ColorProfile::from_hex_colors(vec!["#000000", "#2E1569", "#824DB7", "#C7A1D6"])
|
||||
},
|
||||
|
||||
Self::Veldian => ColorProfile::from_hex_colors(vec![
|
||||
"#D182A8", "#FAF6E0", "#69ACBE", "#5D448F", "#3A113E",
|
||||
]),
|
||||
|
||||
Self::Solian => ColorProfile::from_hex_colors(vec![
|
||||
"#FFF8ED", "#FFE7A8", "#F1B870", "#A56058", "#46281E",
|
||||
]),
|
||||
|
||||
Self::Lunian => ColorProfile::from_hex_colors(vec![
|
||||
"#2F0E62", "#6F41B1", "#889FDF", "#7DDFD5", "#D2F2E2",
|
||||
]),
|
||||
|
||||
// pulled from https://polyamproud.com/flag
|
||||
Self::Polyam => ColorProfile::from_hex_colors(vec![
|
||||
"#FFFFFF", "#FCBF00", "#009FE3", "#E50051", "#340C46",
|
||||
]),
|
||||
|
||||
Self::Sapphic => ColorProfile::from_hex_colors(vec![
|
||||
"#FD8BA8", "#FBF2FF", "#C76BC5", "#FDD768", "#C76BC5", "#FBF2FF", "#FD8BA8",
|
||||
]),
|
||||
|
||||
Self::Androgyne => ColorProfile::from_hex_colors(vec!["#FE007F", "#9832FF", "#00B8E7"]),
|
||||
|
||||
Self::Interprogress => ColorProfile::from_hex_colors(vec![
|
||||
"#FFD800", "#7902AA", "#FFFFFF", "#FFAFC8", "#74D7EE", "#613915", "#000000",
|
||||
"#E50000", "#FF8D00", "#FFEE00", "#028121", "#004CFF", "#770088",
|
||||
]),
|
||||
|
||||
Self::Progress => ColorProfile::from_hex_colors(vec![
|
||||
"#FFFFFF", "#FFAFC8", "#74D7EE", "#613915", "#000000", "#E50000", "#FF8D00",
|
||||
"#FFEE00", "#028121", "#004CFF", "#770088",
|
||||
]),
|
||||
|
||||
Self::Intersex => ColorProfile::from_hex_colors(vec!["#FFD800", "#7902AA", "#FFD800"])
|
||||
.and_then(|c| c.with_weights(vec![2, 1, 2])),
|
||||
|
||||
Self::OldPolyam => ColorProfile::from_hex_colors(vec![
|
||||
"#0000FF", "#FF0000", "#FFFF00", "#FF0000", "#000000",
|
||||
]),
|
||||
|
||||
Self::EqualRights => ColorProfile::from_hex_colors(vec![
|
||||
"#0000FF", "#FFFF00", "#0000FF", "#FFFF00", "#0000FF",
|
||||
])
|
||||
.and_then(|c| c.with_weights(vec![2, 1, 2, 1, 2])),
|
||||
|
||||
Self::Drag => ColorProfile::from_hex_colors(vec![
|
||||
"#CC67FF", "#FFFFFF", "#FFA3E3", "#FFFFFF", "#3366FF",
|
||||
]),
|
||||
|
||||
Self::Pronounfluid => ColorProfile::from_hex_colors(vec![
|
||||
"#FFB3F9", "#FFFFFF", "#D1FDCB", "#C7B0FF", "#000000", "#B8CCFF",
|
||||
]),
|
||||
|
||||
Self::Pronounflux => ColorProfile::from_hex_colors(vec![
|
||||
"#FDB3F8", "#B6CCFA", "#18DDD3", "#64FF89", "#FF7690", "#FFFFFF",
|
||||
]),
|
||||
|
||||
Self::Exipronoun => {
|
||||
ColorProfile::from_hex_colors(vec!["#1C3D34", "#FFFFFF", "#321848", "#000000"])
|
||||
},
|
||||
|
||||
Self::Neopronoun => {
|
||||
ColorProfile::from_hex_colors(vec!["#BCEC64", "#FFFFFF", "#38077A"])
|
||||
},
|
||||
|
||||
Self::Neofluid => ColorProfile::from_hex_colors(vec![
|
||||
"#FFECA0", "#FFFFFF", "#FFECA0", "#38087A", "#BCEC64",
|
||||
]),
|
||||
|
||||
Self::Genderqueer => {
|
||||
ColorProfile::from_hex_colors(vec!["#B57EDC", "#FFFFFF", "#4A8123"])
|
||||
},
|
||||
|
||||
Self::Beiyang => ColorProfile::from_hex_colors(vec![
|
||||
"#DF1B12", "#FFC600", "#01639D", "#FFFFFF", "#000000",
|
||||
]),
|
||||
|
||||
Self::Burger => ColorProfile::from_hex_colors(vec![
|
||||
"#F3A26A", "#498701", "#FD1C13", "#7D3829", "#F3A26A",
|
||||
]),
|
||||
|
||||
Self::ThroatLozenges => ColorProfile::from_hex_colors(vec![
|
||||
"#2759DA", "#03940D", "#F5F100", "#F59B00", "#B71212",
|
||||
]),
|
||||
|
||||
// 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",
|
||||
]),
|
||||
})
|
||||
.expect("preset color profiles should be valid")
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorProfile {
|
||||
pub fn new(colors: Vec<Srgb<u8>>) -> Self {
|
||||
Self { colors }
|
||||
}
|
||||
|
||||
pub fn from_hex_colors<S>(hex_colors: Vec<S>) -> Result<Self>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let colors = hex_colors
|
||||
.into_iter()
|
||||
.map(|s| s.as_ref().parse())
|
||||
.collect::<Result<_, _>>()
|
||||
.context("failed to parse hex colors")?;
|
||||
Ok(Self::new(colors))
|
||||
}
|
||||
|
||||
/// Maps colors based on weights.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `weights` - Weights of each color (`weights[i]` = how many times
|
||||
/// `colors[i]` appears)
|
||||
pub fn with_weights(&self, weights: Vec<u8>) -> Result<Self> {
|
||||
if weights.len() != self.colors.len() {
|
||||
debug!(?weights, ?self.colors, "length mismatch between `weights` and `colors`");
|
||||
return Err(anyhow!(
|
||||
"`weights` should have the same number of elements as `colors`"
|
||||
));
|
||||
}
|
||||
|
||||
let mut weighted_colors = Vec::new();
|
||||
|
||||
for (i, w) in weights.into_iter().enumerate() {
|
||||
weighted_colors.extend(iter::repeat(self.colors[i]).take(usize::from(w)));
|
||||
}
|
||||
|
||||
Ok(Self::new(weighted_colors))
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors spread to the specified
|
||||
/// length.
|
||||
pub fn with_length(&self, length: NonZeroU8) -> Result<Self> {
|
||||
let orig_len = self.colors.len();
|
||||
let orig_len: NonZeroUsize = orig_len.try_into().expect("`colors` should not be empty");
|
||||
let orig_len: NonZeroU8 = orig_len
|
||||
.try_into()
|
||||
.expect("`colors` should not have more than 255 elements");
|
||||
// TODO: I believe weird things can happen because of this...
|
||||
// if length < orig_len {
|
||||
// unimplemented!("compressing length of color profile not implemented");
|
||||
// }
|
||||
let center_i = usize::from(orig_len.get() / 2);
|
||||
|
||||
// How many copies of each color should be displayed at least?
|
||||
let repeats = length.get().div_euclid(orig_len.get());
|
||||
let mut weights = vec![repeats; NonZeroUsize::from(orig_len).get()];
|
||||
|
||||
// How many extra spaces left?
|
||||
let mut extras = length.get().rem_euclid(orig_len.get());
|
||||
|
||||
// If there is an odd space left, extend the center by one space
|
||||
if extras % 2 == 1 {
|
||||
weights[center_i] = weights[center_i].checked_add(1).unwrap();
|
||||
extras = extras.checked_sub(1).unwrap();
|
||||
}
|
||||
|
||||
// Add weight to border until there's no space left (extras must be even at this
|
||||
// point)
|
||||
let weights_len = weights.len();
|
||||
for border_i in 0..usize::from(extras / 2) {
|
||||
weights[border_i] = weights[border_i].checked_add(1).unwrap();
|
||||
let border_opp = weights_len
|
||||
.checked_sub(border_i)
|
||||
.unwrap()
|
||||
.checked_sub(1)
|
||||
.unwrap();
|
||||
weights[border_opp] = weights[border_opp].checked_add(1).unwrap();
|
||||
}
|
||||
|
||||
self.with_weights(weights)
|
||||
}
|
||||
|
||||
/// Colors a text.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `foreground_background` - Whether the color is shown on the foreground
|
||||
/// text or the background block
|
||||
/// * `space_only` - Whether to only color spaces
|
||||
pub fn color_text<S>(
|
||||
&self,
|
||||
txt: S,
|
||||
color_mode: AnsiMode,
|
||||
foreground_background: ForegroundBackground,
|
||||
space_only: bool,
|
||||
) -> Result<String>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let txt = txt.as_ref();
|
||||
|
||||
let txt: Vec<&str> = txt.graphemes(true).collect();
|
||||
|
||||
let ColorProfile { colors } = {
|
||||
let length = txt.len();
|
||||
let length: NonZeroUsize = length.try_into().context("`txt` should not be empty")?;
|
||||
let length: NonZeroU8 = length.try_into().with_context(|| {
|
||||
format!(
|
||||
"`txt` should not have more than {limit} characters",
|
||||
limit = u8::MAX
|
||||
)
|
||||
})?;
|
||||
self.with_length(length)
|
||||
.with_context(|| format!("failed to spread color profile to length {length}"))?
|
||||
};
|
||||
|
||||
let mut buf = String::new();
|
||||
for (i, &gr) in txt.iter().enumerate() {
|
||||
if space_only && gr != " " {
|
||||
if i > 0 && txt[i.checked_sub(1).unwrap()] == " " {
|
||||
buf.push_str("\x1b[39;49m");
|
||||
}
|
||||
buf.push_str(gr);
|
||||
} else {
|
||||
buf.push_str(&colors[i].to_ansi_string(color_mode, foreground_background));
|
||||
buf.push_str(gr);
|
||||
}
|
||||
}
|
||||
|
||||
buf.push_str("\x1b[39;49m");
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors lightened by a multiplier.
|
||||
pub fn lighten(&self, multiplier: f32) -> Self {
|
||||
let mut rgb_f32_colors: Vec<LinSrgb> =
|
||||
self.colors.iter().map(|c| c.into_linear()).collect();
|
||||
|
||||
{
|
||||
let okhsl_f32_colors: &mut [Okhsl] = &mut rgb_f32_colors.into_color_mut();
|
||||
|
||||
for okhsl_f32_color in okhsl_f32_colors {
|
||||
okhsl_f32_color.lightness *= multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
let rgb_u8_colors: Vec<_> = rgb_f32_colors
|
||||
.into_iter()
|
||||
.map(Srgb::<u8>::from_linear)
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
colors: rgb_u8_colors,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors set to the specified
|
||||
/// [`Okhsl`] lightness value.
|
||||
pub fn with_lightness(&self, assign_lightness: AssignLightness) -> Self {
|
||||
let mut rgb_f32_colors: Vec<LinSrgb> =
|
||||
self.colors.iter().map(|c| c.into_linear()).collect();
|
||||
|
||||
{
|
||||
let okhsl_f32_colors: &mut [Okhsl] = &mut rgb_f32_colors.into_color_mut();
|
||||
|
||||
for okhsl_f32_color in okhsl_f32_colors {
|
||||
match assign_lightness {
|
||||
AssignLightness::Replace(lightness) => {
|
||||
okhsl_f32_color.lightness = lightness.into();
|
||||
},
|
||||
AssignLightness::ClampMax(lightness) => {
|
||||
okhsl_f32_color.lightness.clamp_max_assign(lightness.into());
|
||||
},
|
||||
AssignLightness::ClampMin(lightness) => {
|
||||
okhsl_f32_color.lightness.clamp_min_assign(lightness.into());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rgb_u8_colors: Vec<Srgb<u8>> = rgb_f32_colors
|
||||
.into_iter()
|
||||
.map(Srgb::<u8>::from_linear)
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
colors: rgb_u8_colors,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new color profile, with the colors set to the specified
|
||||
/// [`Okhsl`] lightness value, adapted to the terminal theme.
|
||||
pub fn with_lightness_adaptive(&self, lightness: Lightness, theme: TerminalTheme) -> Self {
|
||||
match theme {
|
||||
TerminalTheme::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)),
|
||||
TerminalTheme::Light => self.with_lightness(AssignLightness::ClampMax(lightness)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates another color profile with only the unique colors.
|
||||
pub fn unique_colors(&self) -> Self {
|
||||
let unique_colors: IndexSet<[u8; 3]> = self.colors.iter().map(|&c| c.into()).collect();
|
||||
let unique_colors: Vec<Srgb<u8>> = unique_colors.into_iter().map(|c| c.into()).collect();
|
||||
Self::new(unique_colors)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
use std::fmt::Write as _;
|
||||
use std::io::{self, Write as _};
|
||||
use std::num::{NonZeroU16, NonZeroUsize, Wrapping};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{cmp, thread};
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use crossterm::execute;
|
||||
use crossterm::terminal::{
|
||||
BeginSynchronizedUpdate, EndSynchronizedUpdate, EnterAlternateScreen, LeaveAlternateScreen,
|
||||
};
|
||||
use palette::blend::Blend as _;
|
||||
use palette::{LinSrgba, Srgb, WithAlpha as _};
|
||||
use strum::VariantArray as _;
|
||||
use terminal_size::{terminal_size, Height, Width};
|
||||
|
||||
use crate::color_util::{color, ForegroundBackground, ToAnsiString as _};
|
||||
use crate::neofetch_util::ascii_size;
|
||||
use crate::presets::Preset;
|
||||
use crate::types::AnsiMode;
|
||||
|
||||
const TEXT_ASCII: &str = r"
|
||||
.======================================================.
|
||||
| . . .__ . . . , . | |
|
||||
| |__| _.._ ._ . [__)._.* _| _ |\/| _ ._ -+-|_ | |
|
||||
| | |(_][_)[_)\_| | [ |(_](/, | |(_)[ ) | [ ) * |
|
||||
| | | ._| |
|
||||
'======================================================'
|
||||
";
|
||||
|
||||
const TEXT_ASCII_SMALL: &str = r"
|
||||
.====================.
|
||||
| Happy Pride Month! |
|
||||
'===================='
|
||||
";
|
||||
|
||||
const NOTICE: &str = "Press enter to continue";
|
||||
|
||||
pub fn start_animation(color_mode: AnsiMode) -> Result<()> {
|
||||
let (w, h) = {
|
||||
let (Width(w), Height(h)) = terminal_size().context("failed to get terminal size")?;
|
||||
let w: NonZeroU16 = w.try_into().context("terminal width should not be 0")?;
|
||||
let h: NonZeroU16 = h.try_into().context("terminal height should not be 0")?;
|
||||
(w, h)
|
||||
};
|
||||
|
||||
let text = &TEXT_ASCII[1..TEXT_ASCII.len().checked_sub(1).unwrap()];
|
||||
let (text_width, text_height) =
|
||||
ascii_size(text).expect("text ascii should have valid width and height");
|
||||
let (text, text_width, text_height) = {
|
||||
const TEXT_BORDER_WIDTH: u16 = 2;
|
||||
const NOTICE_BORDER_WIDTH: u16 = 1;
|
||||
const VERTICAL_MARGIN: u16 = 1;
|
||||
let notice_w = NOTICE.len();
|
||||
let notice_w: u8 = notice_w
|
||||
.try_into()
|
||||
.expect("`NOTICE` width should fit in `u8`");
|
||||
let notice_h = NOTICE.lines().count();
|
||||
let notice_h: u8 = notice_h
|
||||
.try_into()
|
||||
.expect("`NOTICE` height should fit in `u8`");
|
||||
let term_w_min = cmp::max(
|
||||
u16::from(text_width)
|
||||
.checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap())
|
||||
.unwrap(),
|
||||
u16::from(notice_w)
|
||||
.checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
let term_h_min = u16::from(text_height)
|
||||
.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 {
|
||||
(text, text_width, text_height)
|
||||
} else {
|
||||
let text = &TEXT_ASCII_SMALL[1..TEXT_ASCII_SMALL.len().checked_sub(1).unwrap()];
|
||||
let (text_width, text_height) =
|
||||
ascii_size(text).expect("text ascii should have valid width and height");
|
||||
let term_w_min = cmp::max(
|
||||
u16::from(text_width)
|
||||
.checked_add(TEXT_BORDER_WIDTH.checked_mul(2).unwrap())
|
||||
.unwrap(),
|
||||
u16::from(notice_w)
|
||||
.checked_add(NOTICE_BORDER_WIDTH.checked_mul(2).unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
let term_h_min = u16::from(text_height)
|
||||
.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 {
|
||||
return Err(anyhow!(
|
||||
"terminal size should be at least ({term_w_min} * {term_h_min})"
|
||||
));
|
||||
}
|
||||
(text, text_width, text_height)
|
||||
}
|
||||
};
|
||||
let text_lines: Vec<&str> = text.lines().collect();
|
||||
|
||||
const BLOCKS: u8 = 9;
|
||||
let block_width: NonZeroU16 = w
|
||||
.get()
|
||||
.div_euclid(u16::from(BLOCKS))
|
||||
.try_into()
|
||||
.with_context(|| format!("terminal width should be at least {BLOCKS}"))?;
|
||||
|
||||
let text_start_y = h
|
||||
.get()
|
||||
.div_euclid(2)
|
||||
.checked_sub((text_height / 2).into())
|
||||
.unwrap();
|
||||
let text_end_y = text_start_y.checked_add(text_height.into()).unwrap();
|
||||
let text_start_x = w
|
||||
.get()
|
||||
.div_euclid(2)
|
||||
.checked_sub((text_width / 2).into())
|
||||
.unwrap();
|
||||
let text_end_x = text_start_x.checked_add(text_width.into()).unwrap();
|
||||
|
||||
let notice_start_x = w
|
||||
.get()
|
||||
.checked_sub(
|
||||
u8::try_from(NOTICE.len())
|
||||
.expect("`NOTICE` length should fit in `u8`")
|
||||
.into(),
|
||||
)
|
||||
.unwrap()
|
||||
.checked_sub(1)
|
||||
.unwrap();
|
||||
let notice_end_x = w.get().checked_sub(1).unwrap();
|
||||
let notice_y = h.get().checked_sub(1).unwrap();
|
||||
|
||||
// Add every preset to colors
|
||||
let colors: Vec<Srgb<u8>> = Preset::VARIANTS
|
||||
.iter()
|
||||
.flat_map(|p| p.color_profile().colors)
|
||||
.collect();
|
||||
|
||||
let fg: Srgb<u8> = "#FFE09B"
|
||||
.parse()
|
||||
.expect("foreground color hex should be valid");
|
||||
let black = LinSrgba::new(0.0, 0.0, 0.0, 0.5);
|
||||
|
||||
let draw_frame = |frame: usize| -> Result<()> {
|
||||
execute!(io::stdout(), BeginSynchronizedUpdate)
|
||||
.context("failed to begin synchronized update")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
// Loop over the height
|
||||
for y in 0..h.get() {
|
||||
// Print the starting color
|
||||
write!(
|
||||
buf,
|
||||
"{bg}{fg}",
|
||||
bg = colors[frame
|
||||
.wrapping_add(y.into())
|
||||
.div_euclid(block_width.get().into())
|
||||
.rem_euclid(colors.len())]
|
||||
.to_ansi_string(color_mode, ForegroundBackground::Background),
|
||||
fg = fg.to_ansi_string(color_mode, ForegroundBackground::Foreground)
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Loop over the width
|
||||
for x in 0..w.get() {
|
||||
let idx = frame
|
||||
.wrapping_add(x.into())
|
||||
.wrapping_add(y.into())
|
||||
.wrapping_add_signed((2.0 * (y as f64 + 0.5 * frame as f64).sin()) as isize);
|
||||
let y_text = text_start_y <= y && y < text_end_y;
|
||||
|
||||
let border = 1u16
|
||||
.checked_add(
|
||||
if y == text_start_y || y == text_end_y.checked_sub(1).unwrap() {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let text_bounds_x1 = text_start_x
|
||||
.checked_sub(border)
|
||||
.expect("`text_start_x - border` should not underflow `u16`");
|
||||
let text_bounds_x2 = text_end_x
|
||||
.checked_add(border)
|
||||
.expect("`text_end_x + border` should not overflow `u16`");
|
||||
let notice_bounds_x1 = notice_start_x
|
||||
.checked_sub(1)
|
||||
.expect("`notice_start_x - 1` should not underflow `u16`");
|
||||
let notice_bounds_x2 = notice_end_x
|
||||
.checked_add(1)
|
||||
.expect("`notice_end_x + 1` should not overflow `u16`");
|
||||
|
||||
// If it's a switching point
|
||||
if idx.rem_euclid(NonZeroUsize::from(block_width).get()) == 0
|
||||
|| x == text_bounds_x1
|
||||
|| x == text_bounds_x2
|
||||
|| x == notice_bounds_x1
|
||||
|| x == notice_bounds_x2
|
||||
{
|
||||
// Print the color at the current frame
|
||||
let ci = idx
|
||||
.div_euclid(NonZeroUsize::from(block_width).get())
|
||||
.rem_euclid(colors.len());
|
||||
let c = colors[ci];
|
||||
if (y_text && (text_bounds_x1 <= x) && (x < text_bounds_x2))
|
||||
|| (y == notice_y && notice_bounds_x1 <= x && x < notice_bounds_x2)
|
||||
{
|
||||
let c: LinSrgba = c.with_alpha(1.0).into_linear();
|
||||
let c = Srgb::<u8>::from_linear(c.overlay(black).without_alpha());
|
||||
write!(
|
||||
buf,
|
||||
"{bg}",
|
||||
bg = c.to_ansi_string(color_mode, ForegroundBackground::Background),
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
write!(
|
||||
buf,
|
||||
"{bg}",
|
||||
bg = c.to_ansi_string(color_mode, ForegroundBackground::Background),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// If text should be printed, print text
|
||||
if y_text && text_start_x <= x && x < text_end_x {
|
||||
write!(
|
||||
buf,
|
||||
"{text_char}",
|
||||
text_char = text_lines[usize::from(y.checked_sub(text_start_y).unwrap())]
|
||||
.chars()
|
||||
.nth(usize::from(x.checked_sub(text_start_x).unwrap()))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
} else if y == notice_y && notice_start_x <= x && x < notice_end_x {
|
||||
write!(
|
||||
buf,
|
||||
"{notice_char}",
|
||||
notice_char = NOTICE
|
||||
.chars()
|
||||
.nth(usize::from(x.checked_sub(notice_start_x).unwrap()))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
write!(buf, " ").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// New line if it isn't the last line
|
||||
if y != h.get().checked_sub(1).unwrap() {
|
||||
writeln!(
|
||||
buf,
|
||||
"{reset}",
|
||||
reset = color("&r", color_mode).expect("reset should be valid"),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut stdout = io::stdout().lock();
|
||||
write!(stdout, "{buf}")
|
||||
.and_then(|_| stdout.flush())
|
||||
.context("failed to write to stdout")?;
|
||||
}
|
||||
|
||||
execute!(io::stdout(), EndSynchronizedUpdate)
|
||||
.context("failed to end synchronized update")?;
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let key_pressed = Arc::new(AtomicBool::new(false));
|
||||
|
||||
// TODO: use non-blocking I/O; no need for another thread
|
||||
let _handle = thread::spawn({
|
||||
let key_pressed = Arc::clone(&key_pressed);
|
||||
move || {
|
||||
loop {
|
||||
match io::stdin().lines().next() {
|
||||
Some(Ok(_)) => {
|
||||
key_pressed.store(true, Ordering::Release);
|
||||
break;
|
||||
},
|
||||
Some(Err(err)) => {
|
||||
eprintln!("failed to read line from stdin: {err}");
|
||||
},
|
||||
None => {
|
||||
// EOF
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut frame: Wrapping<usize> = Wrapping(0);
|
||||
|
||||
const SPEED: u8 = 2;
|
||||
let frame_delay = Duration::from_secs_f32(1.0 / 25.0);
|
||||
|
||||
execute!(io::stdout(), EnterAlternateScreen).context("failed to enter alternate screen")?;
|
||||
|
||||
loop {
|
||||
draw_frame(frame.0)?;
|
||||
frame += usize::from(SPEED);
|
||||
thread::sleep(frame_delay);
|
||||
|
||||
if key_pressed.load(Ordering::Acquire) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
execute!(io::stdout(), LeaveAlternateScreen).context("failed to leave alternate screen")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{AsRefStr, EnumString, VariantNames};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, AsRefStr, Deserialize, EnumString, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum AnsiMode {
|
||||
#[serde(rename = "ansi")]
|
||||
#[serde(skip)]
|
||||
#[strum(serialize = "ansi")]
|
||||
#[strum(disabled)]
|
||||
Ansi16,
|
||||
#[serde(rename = "8bit")]
|
||||
#[strum(serialize = "8bit")]
|
||||
Ansi256,
|
||||
Rgb,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Debug,
|
||||
AsRefStr,
|
||||
Deserialize,
|
||||
EnumString,
|
||||
Serialize,
|
||||
VariantNames,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum TerminalTheme {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Debug,
|
||||
AsRefStr,
|
||||
Deserialize,
|
||||
EnumString,
|
||||
Serialize,
|
||||
VariantNames,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Backend {
|
||||
Neofetch,
|
||||
Fastfetch,
|
||||
#[cfg(feature = "macchina")]
|
||||
Macchina,
|
||||
}
|
||||
|
||||
// See https://github.com/Peternator7/strum/issues/244
|
||||
impl VariantNames for AnsiMode {
|
||||
const VARIANTS: &'static [&'static str] = &["8bit", "rgb"];
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
use std::io::Write as _;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::process::ExitStatusExt as _;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitStatus;
|
||||
use std::{env, fs, io};
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use directories::ProjectDirs;
|
||||
#[cfg(windows)]
|
||||
use normpath::PathExt as _;
|
||||
use tracing::debug;
|
||||
|
||||
pub fn get_cache_path() -> Result<PathBuf> {
|
||||
let path = ProjectDirs::from("", "", "hyfetch")
|
||||
.context("failed to get base dirs")?
|
||||
.cache_dir()
|
||||
.to_owned();
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Reads a string from standard input. The trailing newline is stripped.
|
||||
///
|
||||
/// The prompt string, if given, is printed to standard output without a
|
||||
/// trailing newline before reading input.
|
||||
pub fn input<S>(prompt: Option<S>) -> Result<String>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
if let Some(prompt) = prompt {
|
||||
write!(io::stdout(), "{prompt}", prompt = prompt.as_ref())
|
||||
.and_then(|_| io::stdout().flush())
|
||||
.context("failed to write prompt to stdout")?;
|
||||
}
|
||||
|
||||
io::stdin()
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap_or_else(|| Ok(String::new()))
|
||||
.context("failed to read line from stdin")
|
||||
}
|
||||
|
||||
/// Finds a command in `PATH`.
|
||||
///
|
||||
/// Returns the canonicalized / normalized absolute path of the command.
|
||||
pub fn find_in_path<P>(program: P) -> Result<Option<PathBuf>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let program = program.as_ref();
|
||||
|
||||
// Only accept program name, i.e. a relative path with one component
|
||||
if program.parent() != Some(Path::new("")) {
|
||||
return Err(anyhow!("invalid command name {program:?}"));
|
||||
};
|
||||
|
||||
let path_env = env::var_os("PATH").context("`PATH` env var is not set or invalid")?;
|
||||
|
||||
for search_path in env::split_paths(&path_env) {
|
||||
let path = search_path.join(program);
|
||||
let path = find_file(&path)
|
||||
.with_context(|| format!("failed to check existence of file {path:?}"))?;
|
||||
if path.is_some() {
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Finds a file.
|
||||
///
|
||||
/// Returns the canonicalized / normalized absolute path of the file.
|
||||
pub fn find_file<P>(path: P) -> Result<Option<PathBuf>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
|
||||
let metadata = match fs::metadata(path) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||
return Ok(None);
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(err).with_context(|| format!("failed to get metadata for {path:?}"));
|
||||
},
|
||||
};
|
||||
|
||||
if !metadata.is_file() {
|
||||
debug!(?path, "path exists but is not a file");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
path.canonicalize()
|
||||
.with_context(|| format!("failed to canonicalize path {path:?}"))
|
||||
.map(Some)
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
path.normalize()
|
||||
.with_context(|| format!("failed to normalize path {path:?}"))
|
||||
.map(|p| Some(p.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_command_status(status: &ExitStatus) -> Result<()> {
|
||||
if status.success() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let err = if let Some(code) = status.code() {
|
||||
anyhow!("child process exited with status code: {code}")
|
||||
} else {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
anyhow!(
|
||||
"child process terminated by signal: {signal}",
|
||||
signal = status
|
||||
.signal()
|
||||
.expect("either one of status code or signal should be set")
|
||||
)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
unimplemented!("status code not expected to be `None` on non-Unix platforms")
|
||||
}
|
||||
};
|
||||
Err(err)
|
||||
}
|
||||
|
||||
pub(crate) mod index_map_serde {
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::str::FromStr;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use serde::de::{self, DeserializeSeed, MapAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
pub(crate) fn deserialize<'de, D, K, V>(deserializer: D) -> Result<IndexMap<K, V>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
K: Eq + Hash + FromStr,
|
||||
K::Err: fmt::Display,
|
||||
V: Deserialize<'de>,
|
||||
{
|
||||
struct KeySeed<K> {
|
||||
k: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<'de, K> DeserializeSeed<'de> for KeySeed<K>
|
||||
where
|
||||
K: FromStr,
|
||||
K::Err: fmt::Display,
|
||||
{
|
||||
type Value = K;
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Visitor<'_> for KeySeed<K>
|
||||
where
|
||||
K: FromStr,
|
||||
K::Err: fmt::Display,
|
||||
{
|
||||
type Value = K;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
K::from_str(s).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
struct MapVisitor<K, V> {
|
||||
k: PhantomData<K>,
|
||||
v: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<'de, K, V> Visitor<'de> for MapVisitor<K, V>
|
||||
where
|
||||
K: Eq + Hash + FromStr,
|
||||
K::Err: fmt::Display,
|
||||
V: Deserialize<'de>,
|
||||
{
|
||||
type Value = IndexMap<K, V>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut input: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut map = IndexMap::new();
|
||||
while let Some((k, v)) =
|
||||
input.next_entry_seed(KeySeed { k: PhantomData }, PhantomData)?
|
||||
{
|
||||
map.insert(k, v);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(MapVisitor {
|
||||
k: PhantomData,
|
||||
v: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -1,15 +1,16 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH VERSION "1" "December 2023" "Version is 1.4.11" "User Commands"
|
||||
.TH VERSION "1" "October 2024" "Version is 1.99.0" "User Commands"
|
||||
.SH NAME
|
||||
Version \- manual page for Version is 1.4.11
|
||||
Version \- manual page for Version is 1.99.0
|
||||
.SH DESCRIPTION
|
||||
usage: hyfetch [\-h] [\-c] [\-C CONFIG_FILE]
|
||||
.IP
|
||||
[\-p {rainbow,transgender,nonbinary,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,finsexual,unlabeled1,unlabeled2,pangender,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,beiyang,burger}]
|
||||
[\-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
|
||||
[1m[38;5;14mhyfetch[39m[22m \- neofetch with flags <3
|
||||
.SS "options:"
|
||||
@@ -23,7 +24,7 @@ Configure hyfetch
|
||||
\fB\-C\fR CONFIG_FILE, \fB\-\-config\-file\fR CONFIG_FILE
|
||||
Use another config file
|
||||
.TP
|
||||
\fB\-p\fR {rainbow,transgender,nonbinary,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,finsexual,unlabeled1,unlabeled2,pangender,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,beiyang,burger}, \fB\-\-preset\fR {rainbow,transgender,nonbinary,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,finsexual,unlabeled1,unlabeled2,pangender,gendernonconforming1,gendernonconforming2,femboy,tomboy,gynesexual,androsexual,gendervoid,voidgirl,voidboy,nonhuman\-unity,beiyang,burger}
|
||||
\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}
|
||||
Use preset
|
||||
.TP
|
||||
\fB\-m\fR {8bit,rgb}, \fB\-\-mode\fR {8bit,rgb}
|
||||
@@ -59,6 +60,10 @@ Test for a specific distro
|
||||
.TP
|
||||
\fB\-\-ascii\-file\fR ASCII_FILE
|
||||
Use a specific file for the ascii art
|
||||
.TP
|
||||
\fB\-\-print\-font\-logo\fR
|
||||
Print the Font Logo / Nerd Font icon of your distro
|
||||
and exit
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B Version
|
||||
|
||||
+50
-39
@@ -1,7 +1,7 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH NEOFETCH "1" "December 2023" "Neofetch 7.3.11" "User Commands"
|
||||
.TH NEOFETCH "1" "October 2024" "Neofetch 7.98.0" "User Commands"
|
||||
.SH NAME
|
||||
Neofetch \- manual page for Neofetch 7.3.11
|
||||
Neofetch \- manual page for Neofetch 7.98.0
|
||||
.SH SYNOPSIS
|
||||
.B neofetch
|
||||
\fI\,func_name --option "value" --option "value"\/\fR
|
||||
@@ -49,6 +49,9 @@ Hide/Show Package Manager names. (on, tiny, off)
|
||||
\fB\-\-package_separate\fR on/off
|
||||
Whether to separate system/user modes for supported package managers
|
||||
.TP
|
||||
\fB\-\-package_minimal\fR
|
||||
Reduce output of packages list by not showing programming language package managers or Steam games
|
||||
.TP
|
||||
\fB\-\-os_arch\fR on/off
|
||||
Hide/Show OS architecture.
|
||||
.TP
|
||||
@@ -317,45 +320,48 @@ 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,
|
||||
archcraft_ascii, archcraft_minimal, ARCHlabs, ArchMerge, ArchStrike,
|
||||
ArcoLinux, ArseLinux, Artix, Arya, Asahi, AsteroidOS, astOS, Astra
|
||||
Linux, Athena, azos, Bedrock, BigLinux, Bitrig, BlackArch,
|
||||
blackPanther, BLAG, BlankOn, BlueLight, Bodhi, bonsai, BSD,
|
||||
BunsenLabs, CachyOS, Calculate, CalinixOS, Carbs, CBL\-Mariner,
|
||||
CelOS, Center, CentOS, Chakra, ChaletOS, Chapeau, ChonkySealOS,
|
||||
Chrom, Cleanjaro, Clear Linux OS, ClearOS, Clover, Cobalt, Condres,
|
||||
Container Linux by CoreOS, CRUX, Crystal Linux, Cucumber,
|
||||
CutefishOS, CuteOS, CyberOS, dahlia, DarkOs, Darwin, Debian, Deepin,
|
||||
DesaOS, Devuan, DietPi, digital UNIX, DracOS, DragonFly, Drauger,
|
||||
Droidian, Elementary, Elive, EncryptOS, EndeavourOS, Endless, Enso,
|
||||
EuroLinux, EvolutionOS, Exherbo, Exodia Predator OS, Fedora,
|
||||
ArcoLinux, Arkane, ArseLinux, Artix, Arya, Asahi, AsteroidOS, astOS,
|
||||
Astra Linux, Athena, azos, Bedrock, BigLinux, BigLinux_large,
|
||||
Bitrig, BlackArch, BlackMesa, blackPanther, BLAG, BlankOn,
|
||||
BlueLight, Bodhi, bonsai, BSD, BunsenLabs, CachyOS, Calculate,
|
||||
CalinixOS, Carbs, CBL\-Mariner, CelOS, Center, CentOS, Chakra,
|
||||
ChaletOS, Chapeau, Chimera, ChonkySealOS, Chrom, Cleanjaro, Clear
|
||||
Linux OS, ClearOS, Clover, Cobalt, Condres, Container Linux by
|
||||
CoreOS, CRUX, Crystal Linux, Cucumber, CutefishOS, CuteOS, CyberOS,
|
||||
dahlia, DarkOs, Darwin, Debian, Deepin, DesaOS, Devuan, DietPi,
|
||||
digital UNIX, DracOS, DragonFly, Drauger, Droidian, Elementary,
|
||||
Elive, EncryptOS, EndeavourOS, Endless, Enso, EuroLinux,
|
||||
EvolutionOS, eweOS, Exherbo, Exodia Predator OS, Fedora, Fedora
|
||||
Kinoite, Fedora Sericea, Fedora Silverblue, Fedora_unicode,
|
||||
FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeMiNT, Frugalware,
|
||||
Funtoo, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus, gNewSense,
|
||||
GNOME, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, Haiku, HamoniKR,
|
||||
HarDClanZ, Hash, Huayra, Hybrid, HydroOS, Hyperbola, iglunix,
|
||||
instantOS, Interix, IRIX, Itc, januslinux, Kaisen, Kali, KaOS, KDE,
|
||||
Kibojoe, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LainOS,
|
||||
LangitKetujuh, LaxerOS, LEDE, LibreELEC, Linspire, Linux, Linux
|
||||
Lite, Linux Mint, Linux Mint Old, Live Raizo, LMDE, Lubuntu, Lunar,
|
||||
mac, Mageia, MagpieOS, MainsailOS, Mandriva, Manjaro, MassOS,
|
||||
MatuusOS, Maui, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib, NekOS,
|
||||
Neptune, NetBSD, Netrunner, Nitrux, NixOS, Nobara, NomadBSD,
|
||||
Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, Open Source Media
|
||||
Center, OpenBSD, openEuler, OpenIndiana, openKylin, openmamba,
|
||||
OpenMandriva, OpenStage, openSUSE, openSUSE Leap, openSUSE
|
||||
Tumbleweed, OPNsense, Oracle, orchid, OS Elbrus, PacBSD, Panwah,
|
||||
Parabola, parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, pearOS,
|
||||
Pengwin, Pentoo, Peppermint, Peropesis, phyOS, PikaOS, Pisi, PNM
|
||||
Linux, Pop!_OS, Porteus, PostMarketOS, Profelis SambaBOX, Proxmox,
|
||||
PuffOS, Puppy, PureOS, Q4OS, Qubes, Qubyt, Quibian, Radix, Raspbian,
|
||||
ravynOS, Reborn OS, Red Star, Redcore, Redhat, Refracted Devuan,
|
||||
Regata, Regolith, RhaymOS, rocky, Rosa, Sabayon, sabotage, Sailfish,
|
||||
SalentOS, Salient OS, Salix, Sasanqua, Scientific, semc, Septor,
|
||||
Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel,
|
||||
Funtoo, Furreto, GalliumOS, Garuda, Gentoo, GhostBSD, glaucus,
|
||||
gNewSense, GNOME, GNU, GoboLinux, GrapheneOS, Grombyang, Guix,
|
||||
Haiku, HamoniKR, HarDClanZ, Hash, Huayra, Hybrid, HydroOS,
|
||||
Hyperbola, iglunix, instantOS, Interix, IRIX, Ironclad, Itc,
|
||||
januslinux, Kaisen, Kali, KaOS, KDE, Kibojoe, Kogaion, Korora,
|
||||
KrassOS, KSLinux, Kubuntu, LainOS, LangitKetujuh, LaxerOS, LEDE,
|
||||
LibreELEC, Linspire, Linux, Linux Lite, Linux Mint, Linux Mint Old,
|
||||
LinuxFromScratch, Live Raizo, LMDE, Lubuntu, Lunar, mac, MacaroniOS,
|
||||
Mageia, Magix, MagpieOS, MainsailOS, Mandriva, Manjaro, MassOS,
|
||||
MatuusOS, Maui, Mauna, Meowix, Mer, Minix, MIRACLE LINUX, MX, Namib,
|
||||
NekOS, Neptune, NetBSD, Netrunner, Nitrux, NixOS, nixos_colorful,
|
||||
Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, Open
|
||||
Source Media Center, OpenBSD, openEuler, OpenIndiana, openKylin,
|
||||
openmamba, OpenMandriva, OpenStage, openSUSE, openSUSE Leap,
|
||||
openSUSE Tumbleweed, OPNsense, Oracle, orchid, OS Elbrus, PacBSD,
|
||||
Panwah, Parabola, parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS,
|
||||
pearOS, Pengwin, Pentoo, Peppermint, Peropesis, phyOS, PikaOS, Pisi,
|
||||
PNM Linux, Pop!_OS, Porteus, PostMarketOS, Profelis SambaBOX,
|
||||
Proxmox, PuffOS, Puppy, PureOS, Q4OS, Qubes, Qubyt, Quibian, Radix,
|
||||
Raspbian, ravynOS, Reborn OS, Red Star, Redcore, Redhat, Refracted
|
||||
Devuan, Regata, Regolith, RhaymOS, rocky, Rosa, Sabayon, sabotage,
|
||||
Sailfish, SalentOS, Salient OS, Salix, Sasanqua, Scientific, semc,
|
||||
Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel,
|
||||
Slackware, SliTaz, SmartOS, Soda, Solus, Source Mage, Sparky, Star,
|
||||
SteamOS, Stock Linux, Sulin, SunOS, SwagArch, t2, Tails, TeArch,
|
||||
TorizonCore, Trisquel, Twister, Ubuntu, Ubuntu Budgie, Ubuntu
|
||||
Cinnamon, Ubuntu Kylin, Ubuntu MATE, Ubuntu Studio, Ubuntu Sway,
|
||||
Ubuntu Touch, Ubuntu\-GNOME, ubuntu_old02, Ultramarine Linux,
|
||||
SteamOS, Stock Linux, Sulin, SunOS, SwagArch, t2, Tails, Tatra,
|
||||
TeArch, TorizonCore, Trisquel, Twister, Ubuntu, Ubuntu Budgie,
|
||||
Ubuntu Cinnamon, Ubuntu Kylin, Ubuntu MATE, Ubuntu Studio, Ubuntu
|
||||
Sway, Ubuntu Touch, Ubuntu\-GNOME, ubuntu_old02, Ultramarine Linux,
|
||||
unicodearch, Univalent, Univention, Uos, UrukOS, uwuntu, Vanilla,
|
||||
Venom, VNux, Void, VzLinux, wii\-linux\-ngx, Windows, Windows 10,
|
||||
Windows 11, Windows95, Wrt, Xenia, Xenia2, XFerience, Xray_OS,
|
||||
@@ -371,6 +377,11 @@ linuxlite, linuxmint, mac, mageia, MainsailOS, manjaro, mx, netbsd,
|
||||
nixos, openbsd, opensuse, orchid, parabola, popos, postmarketos,
|
||||
pureos, Raspbian, rocky, slackware, sunos, ubuntu, venom, void have
|
||||
\&'small' logo variants, use {distro}_small to use them.
|
||||
.IP
|
||||
NOTE: Fedora has immutable spins with unique logos (except Onyx).
|
||||
.TP
|
||||
NOTE: Change this to Silverblue, Kinoite, Sericea, or CoreOS to use
|
||||
the spins.
|
||||
.TP
|
||||
\fB\-\-ascii_bold\fR on/off
|
||||
Whether or not to bold the ascii logo.
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
cd /ws/Others/hyfetch
|
||||
python3 -m hyfetch --print-font-logo
|
||||
+8
-7
@@ -1,9 +1,10 @@
|
||||
from . import main
|
||||
from .color_util import printc
|
||||
import os
|
||||
|
||||
from .py import run_py
|
||||
from .rs import run_rust
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main.run()
|
||||
except KeyboardInterrupt:
|
||||
printc('&cThe program is interrupted by ^C, exiting...')
|
||||
exit(0)
|
||||
if os.environ.get('HYFETCH_PY', False):
|
||||
run_py()
|
||||
else:
|
||||
run_rust()
|
||||
|
||||
@@ -1 +1 @@
|
||||
VERSION = '1.4.11'
|
||||
VERSION = '1.99.0'
|
||||
|
||||
@@ -31,6 +31,7 @@ IS_WINDOWS = platform.system() == 'Windows'
|
||||
|
||||
CACHE_PATH = Path(os.getenv("LOCALAPPDATA") or os.getenv("XDG_CACHE_HOME") or Path.home() / '.cache') / 'hyfetch'
|
||||
|
||||
|
||||
@dataclass
|
||||
class GlobalConfig:
|
||||
# Global color mode default to 8-bit for compatibility
|
||||
@@ -49,6 +50,4 @@ class GlobalConfig:
|
||||
return 0.65 if term.lower() == 'dark' else 0.4
|
||||
|
||||
|
||||
GLOBAL_CFG = GlobalConfig(color_mode='8bit', override_distro=None, debug=False, is_light=False, use_overlay=False)
|
||||
|
||||
MINGIT_URL = 'https://github.com/git-for-windows/git/releases/download/v2.37.2.windows.2/MinGit-2.37.2.2-busybox-32-bit.zip'
|
||||
GLOBAL_CFG = GlobalConfig(color_mode='8bit', override_distro=None, debug=False, is_light=False, use_overlay=True)
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"Alma": "",
|
||||
"Alpine": "",
|
||||
"AOSC OS": "",
|
||||
"Apple": "",
|
||||
"Archcraft": "",
|
||||
"ArchLabs": "",
|
||||
"Arch": "",
|
||||
"Arco": "",
|
||||
"Arduino": "",
|
||||
"Artix": "",
|
||||
"Awesome WM": "",
|
||||
"Big": "",
|
||||
"bspwm": "",
|
||||
"Budgie": "",
|
||||
"CentOS": "",
|
||||
"Cinnamon": "",
|
||||
"Codeberg": "",
|
||||
"CoreOS": "",
|
||||
"Crystal": "",
|
||||
"Debian": "",
|
||||
"Deepin": "",
|
||||
"Devuan": "",
|
||||
"Docker": "",
|
||||
"dwm": "",
|
||||
"elementary OS": "",
|
||||
"Endeavour OS": "",
|
||||
"Enlightenment": "",
|
||||
"F-droid": "",
|
||||
"Fedora": "",
|
||||
"Fedora (inverse)": "",
|
||||
"Ferris": "",
|
||||
"Flathub": "",
|
||||
"Fluxbox": "",
|
||||
"Forgejo": "",
|
||||
"FOSDEM": "",
|
||||
"FreeBSD": "",
|
||||
"FreeCAD": "",
|
||||
"freedesktop.org": "",
|
||||
"Garuda": "",
|
||||
"Gentoo": "",
|
||||
"GIMP": "",
|
||||
"Gitea": "",
|
||||
"GNOME": "",
|
||||
"GNU Guix": "",
|
||||
"GTK": "",
|
||||
"Hyperbola -libre": "",
|
||||
"Hyprland": "",
|
||||
"i3": "",
|
||||
"illumos": "",
|
||||
"Inkscape": "",
|
||||
"JWM": "",
|
||||
"Kali": "",
|
||||
"KDE": "",
|
||||
"KDE Neon": "",
|
||||
"KDE Plasma": "",
|
||||
"Kdenlive": "",
|
||||
"KiCad": "",
|
||||
"Krita": "",
|
||||
"Kubuntu": "",
|
||||
"Kubuntu (inverse)": "",
|
||||
"Mint": "",
|
||||
"Mint (inverse)": "",
|
||||
"Loc-OS": "",
|
||||
"LXDE": "",
|
||||
"LXLE": "",
|
||||
"LXQt": "",
|
||||
"Mageia": "",
|
||||
"Mandriva": "",
|
||||
"Manjaro": "",
|
||||
"MATE": "",
|
||||
"mpv": "",
|
||||
"MX": "",
|
||||
"Neovim": "",
|
||||
"NixOS": "",
|
||||
"Octoprint": "",
|
||||
"OpenBSD": "",
|
||||
"OpenSCAD": "",
|
||||
"OpenSUSE": "",
|
||||
"OSH": "",
|
||||
"OSHWA": "",
|
||||
"OSI": "",
|
||||
"Parabola -libre": "",
|
||||
"Parrot OS": "",
|
||||
"Pop!_OS": "",
|
||||
"PostmarketOS": "",
|
||||
"Prusa Slicer": "",
|
||||
"Puppy": "",
|
||||
"Qt": "",
|
||||
"Qtile": "",
|
||||
"QubesOS": "",
|
||||
"Raspberry pi": "",
|
||||
"Red Hat": "",
|
||||
"RepRap": "",
|
||||
"RISC-V": "",
|
||||
"Rocky": "",
|
||||
"Sabayon": "",
|
||||
"Slackware": "",
|
||||
"Slackware (inverse)": "",
|
||||
"Snappy": "",
|
||||
"Solus": "",
|
||||
"Sway": "",
|
||||
"Tails": "",
|
||||
"Thunderbird": "",
|
||||
"Tor Browser": "",
|
||||
"Trisquel": "",
|
||||
"Tux": "",
|
||||
"Ubuntu": "",
|
||||
"Ubuntu (inverse)": "",
|
||||
"Vanilla OS": "",
|
||||
"Void": "",
|
||||
"VS Codium": "",
|
||||
"Wayland": "",
|
||||
"Wikimedia": "",
|
||||
"Xero": "",
|
||||
"XFCE": "",
|
||||
"Xmonad": "",
|
||||
"Xorg": "",
|
||||
"Zorin OS": "",
|
||||
"Windows": "\uE62A"
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
adélie = AsciiArt(match=r'''"Adélie"* | "Adelie"*''', color='4 7 6', ascii=r"""
|
||||
adelie = AsciiArt(match=r'''"Adélie"* | "Adelie"*''', color='4 7 6', ascii=r"""
|
||||
${c1} ${c3} ,-^-___
|
||||
${c3} /\\\///
|
||||
${c2}refined.${c1} /\\\\//
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
aosc_os = AsciiArt(match=r'''"AOSC OS"*''', color='4 0 1 3', ascii=r"""
|
||||
aosc_os = AsciiArt(match=r'''"AOSC OS"*''', color='4 0 1 3', ascii=r'''
|
||||
${c2} __
|
||||
${c2}gpBBBBBBBBBP
|
||||
${c2}_gBBBBBBBBBRP
|
||||
@@ -13,12 +13,12 @@ ${c2} __
|
||||
${c4}_w@@@@@@@@@@@@@@@@P" ${c1}_g@@@@@@@P"
|
||||
${c4}_g@@@@@@@@@@@@@@@N"` ${c1}VMNN@NNNM^`
|
||||
${c4}^MMM@@@@@@@@@@@MP" ${c3},ggppww__
|
||||
${c4}`"\"\"\"" ${c3}_wNNNNNNNNNNNNNNNNNNN
|
||||
${c4}`""""" ${c3}_wNNNNNNNNNNNNNNNNNNN
|
||||
${c3}_gBNNNNNNNNNNNNNNNNNP"
|
||||
${c3}_wNNNNNNNNNNNNNNNNNNMP`
|
||||
${c3}_gBNNNNNNNNNNNNNNNNNP"
|
||||
${c3}_wNNNNNNNNNNNNNNNNNNNM^
|
||||
${c3}""Y^^MNNNNNNNNNNNNP`
|
||||
${c3}`"\"\""\"\""
|
||||
""")
|
||||
${c3}`"""""""
|
||||
''')
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
aosc_os_retro = AsciiArt(match=r'''"AOSC OS/Retro"*''', color='4 7 1 3', ascii=r"""
|
||||
${c2} .........
|
||||
...................
|
||||
.....................${c1}################${c2}
|
||||
.............. ....${c1}################${c2}
|
||||
.............. ...${c1}################${c2}
|
||||
............. ..${c1}****************${c2}
|
||||
............ . .${c1}****************${c2}
|
||||
........... ... ${c1}................${c2}
|
||||
.......... ..... ${c1}...............${c2}
|
||||
......... ....... ...
|
||||
.${c3}...... ${c2}.
|
||||
${c3}..... .....${c2}.... ${c4}...........
|
||||
${c3}.... ......${c2}. ${c4}...........
|
||||
${c3}... ....... ${c4}...........
|
||||
${c3}................ ${c4}***********
|
||||
${c3}................ ${c4}###########
|
||||
${c3}****************
|
||||
${c3}################
|
||||
""")
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
aoscosretro_small = AsciiArt(match=r'''"aoscosretro_small"''', color='4 7 1 3', ascii=r"""
|
||||
${c2} _____ ${c1}_____${c2}
|
||||
-' '-${c1}| |${c2}
|
||||
/ ___ ${c1}| |${c2}
|
||||
| / _ \${c1}|_____|${c2}
|
||||
' / /_\ \
|
||||
\ / _____ \${c4}___
|
||||
${c3}|${c2}/_/ ${c3}| ${c4}| |
|
||||
${c3}| | ${c4}|___|
|
||||
${c3}|_____|
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
arkane = AsciiArt(match=r'''"Arkane"*''', color='7 130 237', ascii=r"""
|
||||
${c3} .:..
|
||||
..:::......
|
||||
${c2}.${c3} .${c2}.${c3}.....
|
||||
${c2}+=${c3}...${c2}==${c3}....
|
||||
......:.${c1}:-${c2}:${c3}..${c1}+*${c2}=${c3}....
|
||||
${c2}:----::${c3}......
|
||||
${c2}.=***##*+=: ${c3}..
|
||||
${c2}=${c1}***######*${c2}=
|
||||
${c2}.${c1}-*######+
|
||||
${c2}:+${c1}###%%%###${c3}:
|
||||
${c2}-+*${c1}########+${c3}.
|
||||
${c2}=++*${c1}#######${c3}-
|
||||
${c2}-+=+**${c1}*####${c3}=
|
||||
${c3}.${c2}-=++==***${c1}##*${c1}-
|
||||
${c2}-++++++==++++=
|
||||
.-+++**+++=+===${c3}.
|
||||
${c2}:---===++++=-=--${c3}.
|
||||
${c2}-===============${c3}-==--:
|
||||
${c2}.-==+++***++*${c1}*#########${c3}=:::.
|
||||
${c2}.-=++++*++++**${c1}#######%%###${c3}=
|
||||
${c2}.:==++++++**${c1}#############${c3}:
|
||||
${c2}.${c3}-+*++*+++==${c1}###${c3}+
|
||||
-${c1}*+${c3}:
|
||||
""")
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
aster = AsciiArt(match=r'''"Aster"*''', color='6 6', ascii=r"""
|
||||
${c1} ...''...
|
||||
${c1} .;oOXWMWNXXXNMMN0d:.
|
||||
${c1} .oXMWOo;.. ..:oO;
|
||||
${c1} ;KMWx, co,
|
||||
${c1} 'KMNl dMMW.
|
||||
${c1} oMMx xMMMMk
|
||||
${c1} xMM: dMMMMMM;
|
||||
${c1} cMMl dMMMMMMMW
|
||||
${c1} NMK xMMMx::dXMx
|
||||
${c1} ,MMl xMMN' .o.
|
||||
${c1} cMM; dMMW'
|
||||
${c1} ;MMc oMMW,
|
||||
${c1} WMK dMMW, ccccccc.
|
||||
${c1} lMMl oMMM; ooooooo.
|
||||
${c1} OMMc ...
|
||||
${c1} xMMx
|
||||
${c1} ;XMN:
|
||||
${c1} ,.
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
biglinux_large = AsciiArt(match=r'''"BigLinux_large"*''', color='2 3 4 7', ascii=r"""
|
||||
${c4}
|
||||
${c3}.cd0KXXX${c4}0${c3}x;
|
||||
${c3}.oXM${c4}M${c3}MMMMMMMMMWo
|
||||
${c2}... ${c3}dWMMMM${c4}M${c3}MMMMMMMMMMN,
|
||||
${c2}c0WMMMW0: ${c3}.XMMMM${c4}M${c3}MMMKdc;,;cxNMM:
|
||||
${c1}x, ${c2}.XMMMMMMMMM: ${c3}'W${c3}MMMMMMWo. ;KW.
|
||||
${c1}dM. ${c2}.WMMMMMMMM0 ${c3}N${c3}MMMM${c4}M${c3}Mk. dd
|
||||
${c1}.MM. ${c2}.lOKXKOl. ${c3}oMMMMMMd .l${c4}x${c3}kd: .
|
||||
${c1}kMM: .'.. ${c2}:xOOxc. ${c3}O${c4}M${c3}MMMM0 lXMMMMM${c4}M${c3}N;
|
||||
${c1}WMM0 lKMMMMMW0o. ${c2}KMMMMMMN. ${c3}xMM${c4}M${c3}M, .dWMMMMMMMMMM;
|
||||
${c1}.MMMMx .oWMMMMMMOl0MMX; ${c2}NMMMMMMM; ${c3}.NMMMM. .dWMMMM${c4}M${c3}MMMMMMMN
|
||||
${c1}.MMMMMNOKMMMMMMMk. 'MMMMx ${c2};MMMMMMMO ${c3}.KMMMl .oWMMMMMWk:,lWMMMM.
|
||||
${c1}xMMMMMMMMMMMMk. .MMMMMc ${c2}'XMMMMMMx ${c3}:0MMNMMMM${c4}M${c3}MWx. .WM${c4}M${c3}M.
|
||||
${c1}0MMMMMMMMWx. :MMMMMK ${c2}cKMMMMMX: ${c3},ok0K0x:. oMMN
|
||||
${c1}oNMMMM0c. .NMMMMMO ${c2}.lOWMMMX: ${c3}.M${c4}M${c3}o
|
||||
${c1}c. .'. .KMMMMMM: ${c2}.';clc. ${c3}MN
|
||||
${c1}:N; cWMMMMMMO ${c3}.W;
|
||||
${c1}0M0: 'dNMMMMMMM0 ${c3}',
|
||||
${c1}0MMMX0O0XMMMMMMMMMMo
|
||||
${c1}oWMMMMMMMMMMMMMMk.
|
||||
${c1}.oXMMMMMMMMW0c.
|
||||
${c1}.;ccc:,.
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
blackmesa = AsciiArt(match=r'''"BlackMesa"''', color='1', ascii=r"""
|
||||
${c1}
|
||||
.-;+$XHHHHHHX$+;-.
|
||||
,;X@@X%/;=----=:/%X@@X/,
|
||||
=$@@%=. .=+H@X:
|
||||
-XMX: =XMX=
|
||||
/@@: =H@+
|
||||
%@X, .$@$
|
||||
+@X. $@%
|
||||
-@@, .@@=
|
||||
%@% +@$
|
||||
H@: :@H
|
||||
H@: :HHHHHHHHHHHHHHHHHHX, =@H
|
||||
%@% ;@M@@@@@@@@@@@@@@@@@H- +@$
|
||||
=@@, :@@@@@@@@@@@@@@@@@@@@@= .@@:
|
||||
+@X :@@@@@@@@@@@@@@@M@@@@@@:%@%
|
||||
$@$, ;@@@@@@@@@@@@@@@@@M@@@@@@$.
|
||||
+@@HHHHHHH@@@@@@@@@@@@@@@@@@@@@@@+
|
||||
=X@@@@@@@@@@@@@@@@@@@@@@@@@@@@X=
|
||||
:$@@@@@@@@@@@@@@@@@@@M@@@@$:
|
||||
,;$@@@@@@@@@@@@@@@@@@X/-
|
||||
.-;+$XXHHHHHX$+;-.
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
chimera = AsciiArt(match=r'''"Chimera"*''', color='1 5 4 1', ascii=r"""
|
||||
${c3}ddddddddddddddc ${c1},cc:
|
||||
${c3}ddddddddddddddc ${c1},cc:
|
||||
${c3}ddddddddddddddd ${c1},cc:
|
||||
${c3}ddddddddddddl:' ${c1},cc:
|
||||
${c3}dddddddddl' ${c1}..;cc:
|
||||
${c3}dddddddo. ${c1},:cccccc:
|
||||
${c3}ddddddl ${c1},ccc:'''''
|
||||
${c3}dddddo. ${c1};ccc. ............
|
||||
.ccc. cccccccccccc
|
||||
${c2}...... ${c1}.ccc. .ccc'''''''''
|
||||
${c2}OOOOOk. ${c1};ccc. .ccc; ......
|
||||
${c2}OOOOOOd ${c1}'ccc:,....,:ccc' ${c4}coooooo
|
||||
${c2}OOOOOOOx. ${c1}':cccccccc:' ${c4}.looooooo
|
||||
${c2}OOOOOOOOOd, ${c1}`'''` ${c4}.coooooooo
|
||||
${c2}OOOOOOOOOOOOdc,. ${c4}..,coooooooooooo
|
||||
${c2}OOOOOOOOOOOOOOOO' ${c4}.oooooooooooooooo
|
||||
${c2}OOOOOOOOOOOOOOOO' ${c4}.oooooooooooooooo
|
||||
${c2}OOOOOOOOOOOOOOOO' ${c4}.oooooooooooooooo
|
||||
""")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
container_linux_by_coreos = AsciiArt(match=r'''"Container Linux by CoreOS"* | "Container_Linux"*''', color='4 7 1', ascii=r"""
|
||||
container_linux_by_coreos = AsciiArt(match=r'''"Container Linux by CoreOS"* | "Container_Linux"* | "Fedora CoreOS" | "CoreOS"*''', color='4 7 1', ascii=r"""
|
||||
${c1} .....
|
||||
.';:cccccccc:;'.
|
||||
':ccccclc${c3}lllllllll${c1}cc:.
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
debian = AsciiArt(match=r'''"Debian"*''', color='1 7 3', ascii=r"""
|
||||
debian = AsciiArt(match=r'''"Debian"*''', color='1 7 3', ascii=r'''
|
||||
${c2} _,met$$$$$gg.
|
||||
,g$$$$$$$$$$$$$$$P.
|
||||
,g$$P" "\""Y$$.".
|
||||
,g$$P" """Y$$.".
|
||||
,$$P' `$$$.
|
||||
',$$P ,ggs. `$$b:
|
||||
`d$$' ,$P"' ${c1}.${c2} $$$
|
||||
@@ -19,6 +19,6 @@ ${c2} `Y$$
|
||||
`$$b.
|
||||
`Y$$b.
|
||||
`"Y$b._
|
||||
`"\""
|
||||
""")
|
||||
`"""
|
||||
''')
|
||||
|
||||
@@ -9,8 +9,8 @@ def detect(name: str) -> AsciiArt | None:
|
||||
return None
|
||||
name = name.lower()
|
||||
if name.startswith('adélie') or name.startswith('adelie'):
|
||||
from .adélie import adélie
|
||||
return adélie
|
||||
from .adelie import adelie
|
||||
return adelie
|
||||
|
||||
if name.startswith('aix'):
|
||||
from .aix import aix
|
||||
@@ -124,6 +124,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .arcolinux import arcolinux
|
||||
return arcolinux
|
||||
|
||||
if name.startswith('arkane'):
|
||||
from .arkane import arkane
|
||||
return arkane
|
||||
|
||||
if name == 'arch_small':
|
||||
from .arch_small import arch_small
|
||||
return arch_small
|
||||
@@ -204,6 +208,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .bedrock import bedrock
|
||||
return bedrock
|
||||
|
||||
if name.startswith('biglinux_large'):
|
||||
from .biglinux_large import biglinux_large
|
||||
return biglinux_large
|
||||
|
||||
if name.startswith('biglinux'):
|
||||
from .biglinux import biglinux
|
||||
return biglinux
|
||||
@@ -216,6 +224,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .blackarch import blackarch
|
||||
return blackarch
|
||||
|
||||
if name == 'blackmesa':
|
||||
from .blackmesa import blackmesa
|
||||
return blackmesa
|
||||
|
||||
if name.startswith('blackpanther') or name.startswith('blackpanther'):
|
||||
from .blackpanther import blackpanther
|
||||
return blackpanther
|
||||
@@ -304,6 +316,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .chapeau import chapeau
|
||||
return chapeau
|
||||
|
||||
if name.startswith('chimera'):
|
||||
from .chimera import chimera
|
||||
return chimera
|
||||
|
||||
if name.startswith('chonkysealos'):
|
||||
from .chonkysealos import chonkysealos
|
||||
return chonkysealos
|
||||
@@ -336,7 +352,7 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .condres import condres
|
||||
return condres
|
||||
|
||||
if name.startswith('container linux by coreos') or name.startswith('container_linux'):
|
||||
if name.startswith('container linux by coreos') or name.startswith('container_linux') or name == 'fedora coreos' or name.startswith('coreos'):
|
||||
from .container_linux_by_coreos import container_linux_by_coreos
|
||||
return container_linux_by_coreos
|
||||
|
||||
@@ -476,6 +492,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .evolutionos import evolutionos
|
||||
return evolutionos
|
||||
|
||||
if name.startswith('eweos'):
|
||||
from .eweos import eweos
|
||||
return eweos
|
||||
|
||||
if name.startswith('exherbo'):
|
||||
from .exherbo import exherbo
|
||||
return exherbo
|
||||
@@ -488,6 +508,22 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .fedora_old import fedora_old
|
||||
return fedora_old
|
||||
|
||||
if name == 'fedora_unicode':
|
||||
from .fedora_unicode import fedora_unicode
|
||||
return fedora_unicode
|
||||
|
||||
if name == 'fedora silverblue' or name.startswith('silverblue'):
|
||||
from .fedora_silverblue import fedora_silverblue
|
||||
return fedora_silverblue
|
||||
|
||||
if name.startswith('fedora kinoite') or name.startswith('kinoite'):
|
||||
from .fedora_kinoite import fedora_kinoite
|
||||
return fedora_kinoite
|
||||
|
||||
if name.startswith('fedora sericea') or name.startswith('sericea'):
|
||||
from .fedora_sericea import fedora_sericea
|
||||
return fedora_sericea
|
||||
|
||||
if name.startswith('fedora'):
|
||||
from .fedora import fedora
|
||||
return fedora
|
||||
@@ -504,6 +540,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .finnix import finnix
|
||||
return finnix
|
||||
|
||||
if name.startswith('furreto'):
|
||||
from .furreto import furreto
|
||||
return furreto
|
||||
|
||||
if name == 'freebsd_small':
|
||||
from .freebsd_small import freebsd_small
|
||||
return freebsd_small
|
||||
@@ -624,6 +664,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .interix import interix
|
||||
return interix
|
||||
|
||||
if name.startswith('ironclad'):
|
||||
from .ironclad import ironclad
|
||||
return ironclad
|
||||
|
||||
if name.startswith('januslinux') or name.startswith('janus') or name.startswith('ataraxia linux') or name.startswith('ataraxia'):
|
||||
from .januslinux import januslinux
|
||||
return januslinux
|
||||
@@ -696,6 +740,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .linux import linux
|
||||
return linux
|
||||
|
||||
if name == 'linuxfromscratch' or name.endswith('lfs') or name == 'linux-from-scratch' or name == 'linux_from_scratch':
|
||||
from .linuxfromscratch import linuxfromscratch
|
||||
return linuxfromscratch
|
||||
|
||||
if name == 'linux_small':
|
||||
from .linux_small import linux_small
|
||||
return linux_small
|
||||
@@ -720,6 +768,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .lunar import lunar
|
||||
return lunar
|
||||
|
||||
if name.startswith('macaronios') or name.startswith('macaroni os'):
|
||||
from .macaronios import macaronios
|
||||
return macaronios
|
||||
|
||||
if name == 'mac"*"_small':
|
||||
from .mac_small import mac_small
|
||||
return mac_small
|
||||
@@ -736,6 +788,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .mageia import mageia
|
||||
return mageia
|
||||
|
||||
if name.startswith('magix'):
|
||||
from .magix import magix
|
||||
return magix
|
||||
|
||||
if name.startswith('magpieos'):
|
||||
from .magpieos import magpieos
|
||||
return magpieos
|
||||
@@ -772,6 +828,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .maui import maui
|
||||
return maui
|
||||
|
||||
if name.startswith('mauna'):
|
||||
from .mauna import mauna
|
||||
return mauna
|
||||
|
||||
if name.startswith('meowix'):
|
||||
from .meowix import meowix
|
||||
return meowix
|
||||
@@ -852,6 +912,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .nixos_old import nixos_old
|
||||
return nixos_old
|
||||
|
||||
if name.startswith('nixos_colorful'):
|
||||
from .nixos_colorful import nixos_colorful
|
||||
return nixos_colorful
|
||||
|
||||
if name.startswith('nixos'):
|
||||
from .nixos import nixos
|
||||
return nixos
|
||||
@@ -1448,6 +1512,10 @@ def detect(name: str) -> AsciiArt | None:
|
||||
from .xenia import xenia
|
||||
return xenia
|
||||
|
||||
if name.startswith('tatra'):
|
||||
from .tatra import tatra
|
||||
return tatra
|
||||
|
||||
if name.startswith('zorin'):
|
||||
from .zorin import zorin
|
||||
return zorin
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
eweos = AsciiArt(match=r'''"eweOS"*''', color='7 11 9 8 1', ascii=r"""
|
||||
${c2} #####%%%
|
||||
${c2} ##%%${c3}////${c2}%%%%%${c3}///
|
||||
${c2} #%%%%${c3}////((((////${c2}%
|
||||
${c1} *@@@@@@@${c3}/${c5},,,${c3}/////${c5},,,${c2}%${c1}@@@@@@@
|
||||
${c1} .@@@@@@@@@@@${c3}////////${c2}%%%${c1}@@@@@@@@@@@@
|
||||
${c1} @@@${c4}...${c1}@@@@@@${c3}////${c2}%%${c3}////${c1}@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@@@@@@@@@@@@@@@@@@
|
||||
${c1} @@@@@@ @@@@@@
|
||||
${c1} @@@ @@@
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
fedora_kinoite = AsciiArt(match=r'''"Fedora Kinoite"* | "Kinoite"*''', color='12 7', ascii=r"""
|
||||
${c1} ,clll:.${c2} .,::::::::::::'
|
||||
${c1}:ooooooo${c2} .;::::::::::::::
|
||||
${c1}looooooo${c2} ,:::::::::::::::'
|
||||
${c1}looooooo${c2} .::::::::::::::::
|
||||
${c1}looooooo${c2} ;:::::::::::::::.
|
||||
${c1}looooooo${c2} .::::::::::::::::
|
||||
${c1}looooool${c2};;;;,::::::::::::::::
|
||||
${c1}looool${c2}::, .::::::::::::::
|
||||
${c1}looooc${c2}:: ;::
|
||||
${c1}looooc${c2}::;. .::;
|
||||
${c1}loooool${c2}:::::::::::.
|
||||
${c1}looooooo${c2}. .::::::'
|
||||
${c1}looooooo${c2} .::::::,;,..
|
||||
${c1}looooooo${c2} :::;' ';:;.
|
||||
${c1}looooooo${c2} ::: :::
|
||||
${c1}cooooooo${c2} .::' '::.
|
||||
${c1}.ooooc ${c2} ::, ,::
|
||||
''''
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
fedora_sericea = AsciiArt(match=r'''"Fedora Sericea"* | "Sericea"*''', color='12 7', ascii=r"""
|
||||
${c1} :oooo, .','
|
||||
.';;;.;oooooooolooooo'
|
||||
coooooooooooooooooooooooolc'
|
||||
.':oooooooooooo${c2}ll${c1}ooooooooooooool
|
||||
.oooooooooooooooo${c2}ll${c1}oooooooooooo${c2}l${c1}ool
|
||||
ooooooooooooooooo${c2}ll${c1}ooooooooooo${c2}ll${c1}oo'
|
||||
oooo${c2}l${c1}oooooooooo${c2}lll${c1}ooooooooo${c2}lll${c1}oo
|
||||
.ooooo${c2}lll${c1}ooooo${c2}lll${c1}ooooooooo${c2}lll${c1}ool
|
||||
.ooooooo${c2}lll${c1}oo${c2}llll${c1}oooo${c2}lllll${c1}ooooo:
|
||||
'oooooooo${c2}llllllllllll${c1}oooooooo'
|
||||
.c,.oo${c2}lllll${c1}oooooooo.${c2}
|
||||
'll;
|
||||
'll.
|
||||
lll
|
||||
lll
|
||||
;ll,
|
||||
.l:
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
fedora_silverblue = AsciiArt(match=r'''"Fedora Silverblue" | "Silverblue"*''', color='4 7 12', ascii=r"""
|
||||
${c1} .;ooooooooooooooooooooooooooo.
|
||||
${c1} ,dddddddddddddddddddddddddddddd'${c3};
|
||||
${c1} lddddddddddddddddddddddddddddd'${c3};;;
|
||||
${c1}ddddd${c2},XXX.${c1}ddddd${c2},XXX.${c1}dddd'${c2},XXX.${c3};;;;;
|
||||
${c1}ddddd${c2}XX${c1}x${c2}XX${c1}ddddd${c2}XX${c1}x${c2}XX${c1}ddd'${c2},XX${c3}x${c2}XX${c3};;;;;
|
||||
${c1}ddddd${c2}'XXX'${c1}ddddd${c2}'XXX'${c1}dd'${c2}XXXXXX'${c3};;;;;
|
||||
${c1}dddddd${c2};X;${c1}ddddddd${c2};X:${c1}d'${c2}XXX${c3};;;;;;;;;;;
|
||||
${c1}dddddd${c2};X;${c1}ddddddd${c2};X:${c2}XXX${c3};;;;;;;;;;;;;
|
||||
${c1}dddddd${c2};X;${c1}dddddd'${c2};XXX,,,,,,XXX.${c3};;;;;
|
||||
${c1}dddddd${c2};X;${c1}dddd'${c2}XXXX${c2}XXXXXXXXX${c3}x${c2}XX${c3};;;;;
|
||||
${c1}dddddd${c2};X;${c1}dd'${c2}XXX${c3};;;;;;;;;;;${c2}XXX${c3};;;;;;
|
||||
${c1}dddddd${c2};X;${c1}'${c2}XXX${c3};;;;;;;;;;;;;;;;;;;;;;
|
||||
${c1}dddddd${c2};XXXXX,,,,,,,,,,,,,;XXX:${c3};;;;;
|
||||
${c1}dddddd${c2}:XXXXXXXXXXXXXXXXXXXX${c3}x${c2}XX${c3};;;;;
|
||||
${c1}ddddd'${c3};;;;;;;;;;;;;;;;;;;${c2}'XXX'${c3};;;;'
|
||||
${c1}ddd'${c3};;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
${c1}o'${c3};;;;;;;;;;;;;;;;;;;;;;;;;;;;'
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
fedora_unicode = AsciiArt(match=r'''"Fedora_unicode"''', color='12 7', ascii=r"""
|
||||
${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}██████████████████▀
|
||||
████████████████████████████████▀
|
||||
▀█████████████████████████████▀
|
||||
▀█████████████████████████▀
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
furreto = AsciiArt(match=r'''"Furreto"*''', color='211 255 225 199', ascii=r"""
|
||||
${c1}
|
||||
${c1}.xOOko .odd,
|
||||
o${c3}X${c2}WWK${c3}O${c1}OOO. 'O${c3}N${c2}WW${c3}0${c1}kkk.
|
||||
.k0${c3}XKO${c1}OOOOOcOO${c2}NWN${c1}OOOOO.
|
||||
xOOOOOOOOOkkOOOOOOOOO;
|
||||
.O0OkkocxO000000kcdk0OO0OOkx
|
||||
k${c3}W${c2}MX${c1}kkkkloxkkkx; :dxxxxddc...
|
||||
'kO0OOOOOkc .cl:..kk0KK0Okc
|
||||
;kOOO0000xd. dO00000Oo .xkO${c3}N${c2}MM${c3}X${c1}OOOO
|
||||
.dddxkOOOkddc.k${c3}K${c2}NWW${c3}N${c1}000000l.ddk${c2}00${c1}00OOOO.
|
||||
'dd:;ddddd;.d${c3}K${c2}MMM${c3}W${c1}K00KKK0O::ddxkO00Oko
|
||||
.okxkOKK0kkOO00KKOxxlodddddddl
|
||||
.00OOkkkkkkkkOOO00OOOO0O; .dddl
|
||||
'kkkkkxxkkkkkkkOOkxdxkxxddd.
|
||||
cddddddddxxkkkkk${c4}xddddddddddo
|
||||
'ddddddodddddddddddddddddddc
|
||||
.ddddddodddddddddodddddddc
|
||||
.odddo.
|
||||
|
||||
.kOOkkk;
|
||||
lkKWNkkkxc
|
||||
kkxkkkkkkx.
|
||||
,,..xxx.
|
||||
""")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
haiku = AsciiArt(match=r'''"Haiku"*''', color='1 3 7 2', ascii=r"""
|
||||
haiku = AsciiArt(match=r'''"Haiku"*''', color='1 3 7 2', ascii=r'''
|
||||
${c3}
|
||||
|
||||
MMMM MMMM
|
||||
@@ -16,6 +16,6 @@ ${c3} MMMM${c4} .cOMMMMM|/MMMMM/`
|
||||
MM${c4}MMMMMMM/`:MMM/ ${c3}MMMM
|
||||
MMMM MMMM
|
||||
MMMM MMMM
|
||||
"\"\"" "\"\""
|
||||
""")
|
||||
"""" """"
|
||||
''')
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
ironclad = AsciiArt(match=r'''"Ironclad"*''', 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
|
||||
""")
|
||||
|
||||
@@ -7,7 +7,7 @@ ${c2} /==\
|
||||
\==/
|
||||
${c1} · · · · · · ·
|
||||
· · · · · · · · · ·
|
||||
· · · ${c2}.-======-.${c1}· · · ·
|
||||
· · · ${c2}.-======-.${c1}· · · ·
|
||||
${c2} .::.${c1} ·${c2}.-============-.${c1}· ${c2}.::.
|
||||
.:==:${c1}· ${c2}.:===:'${c1}. ·· .${c2}':===:.${c1} ·${c2}:==:.
|
||||
.:===:${c1} · ${c2}:===.${c1} · ${c3}.--.${c1} · ${c2}.===:${c1} · ${c2}:===:.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
linuxfromscratch = AsciiArt(match=r'''"LinuxFromScratch" | *"LFS" | "linux-from-scratch" | "linux_from_scratch"''', color='fg 8 3', ascii=r"""
|
||||
${c2} .:@:.
|
||||
${c2} :@@@@@@@:
|
||||
${c2} @@@@@@@@@-
|
||||
${c2} .:%. @@@@@@@@@+. @%
|
||||
${c2} *@@@%+: :@@@@@@@%=: .=%@@@@@@=
|
||||
${c2} :@@@@@@##@@@@@@@@@%*+%@%+@@@@@@@+
|
||||
${c2} @@#${c1}####${c2}+@@@@@@@%:${c1}######${c2}=@@@@@@@@@-
|
||||
${c2} *@%${c1}######${c2}.@@@@@#${c1}#########${c2}-@@@@@@@@#.
|
||||
${c2} %@-${c1}#${c2}.@${c1}=${c2}:${c1}##${c2}+@@@@-${c1}###${c2}%@${c1}:${c2}=${c1}###${c2}*@#*+=-+#:
|
||||
${c2} @@.${c1}#${c2}@@*${c1}=${c2}:${c1}#${c2}-%%**-${c1}##${c2}%@@%${c1}*${c2}*${c1}###${c2}#=-
|
||||
${c2} @@-${c1}#${c2}@@@@+.-${c3}...${c2}:=.${c1}#${c2}%@@@@%${c1}###${c2}#-
|
||||
${c2} %@%${c1}##${c2}*#:${c3}.o.....o...${c2}-%@+${c1}###${c2}#@+ -:
|
||||
${c2} +@@*${c1}#${c3}....................${c2}+@@@@@@@@+
|
||||
${c2} @%:${c3}....................._:${c2}@@@@@@@=.
|
||||
${c2} .=:${c3}...............__*-=`.${c2}=@@@@@@#=.
|
||||
${c2} :+:${c3}....:==*__*-=`:..==-:${c2}#@@@@@%+:
|
||||
${c2} .--=-: ${c3}+..::.....-: ${c2}=%@*=:
|
||||
${c2} :........-
|
||||
${c2} .:...--.
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
macaronios = AsciiArt(match=r'''"MacaroniOS"* | "Macaroni OS"*''', color='2 3 15 14', ascii=r"""
|
||||
${c2}
|
||||
-=+++==--::.. .-+++==-:.
|
||||
.:--:-=++==-:. :=*+=--==+***######**+-:
|
||||
.---:-===-----===+++==-:.. :=+=--=+*****+++*****#%%%#-
|
||||
.=*********+++========+++++=--:. .:-=+++++++=-:.. .=*=::.
|
||||
:-=-. .:::---===+++++++++++*******++====+**+++=-:. :+*-..
|
||||
.. -++ ..:--==+***********=:. -*=
|
||||
. :+. . ${c3}...${c2} .:--===:. .++
|
||||
.*: . ${c3}:+#%%@@%%#=: ${c2}--
|
||||
${c2}-= ${c3}-#@@@@@@@@@@@@#: ${c2}.
|
||||
${c3}:*%@%%@@@@@@@@@@@%+ .:--====-:.
|
||||
:*%%%%%%%%%@@@@@@@@@*. :=*#%%%%@@@@@%#+-
|
||||
.*%%%%%%%%%%%%%@@#+--=- -*%%%%%%%%%%%@@@@@%#=
|
||||
-#%%%%%%%%%%%%%#: :+: :+%*#%%%%%%%%%%%%%%%%%*.
|
||||
+%%%%%%%%%%%%%%: .#*: *+-*%%%%%%%%%%%%%%%%%+
|
||||
+%%%%%%%%%%%%%# . . . :%%%%%%%%%%%%%%%%%#.
|
||||
=#%%%%%%%%%%%%# . . =%%%%%%%%%%%%%%%%%*.
|
||||
.*%%%%%%%%%%%%%* : =%%%%%%%%%%%%%%%%%#-
|
||||
:#%%%%%%%%%%%%%#=.... .=*%%%%%%%%%%%%%%%%%%*:
|
||||
:*%@@%%%%%%%%%%%%#+. :+#%%%%%%%%%%%%%#*-
|
||||
=*%%@@%%%%%%%#*- .-=+**##**+=:
|
||||
:=*######+=:
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
magix = AsciiArt(match=r'''"Magix"*''', color='206 39', ascii=r"""
|
||||
${c2} @
|
||||
@@--=====@@
|
||||
@@--==@@ @@====+@
|
||||
@-@@ @==@
|
||||
@=@
|
||||
@=@${c1} @=@ @-==== @=@${c2}
|
||||
@=@${c1} @-===@==++@===+@${c2}
|
||||
@=@${c1} @--====@@=====+@${c2}
|
||||
-=@${c1} @--==========++@${c2}
|
||||
==${c1} @--==========++@${c2} @=@
|
||||
@==${c1} @--=======@==++@${c2} @=+@
|
||||
@==${c1} @-==========++${c2} @=@
|
||||
@==${c1} @-=======@=%${c2} @=@
|
||||
@==${c1} @@@@@@${c2} @=@
|
||||
@====@@@ @@===+%
|
||||
@@=====@@==++++@@
|
||||
=#@=@
|
||||
@==@++@
|
||||
@@@
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
mauna = AsciiArt(match=r'''"Mauna"*''', color='4 6', ascii=r"""
|
||||
${c1} .. :-=++++=-:
|
||||
.-+*+ -********* **=
|
||||
=***= +******+ =---=+*.
|
||||
+**** +****+ :-=++*++=--
|
||||
=****= **** :+***********+:
|
||||
+****+ *** ${c2}.-- ${c1}+******-
|
||||
=*****: *- ${c2}:+=: ${c1}=*****:
|
||||
${c2}. ${c1}+*****: : ${c2}-+++ ${c1}:****=
|
||||
${c2}-+ ${c1}:*****+ ${c2}- ++++ ${c1}:***-
|
||||
${c2}-++=: ${c1}=+****: ${c2}:++ +++++ ${c1}**+
|
||||
${c2} +++++= ${c1}-====-${c2}++++: +++++ ${c1}*+
|
||||
${c2} .=++++++++++++++: ${c2}+++++= ${c1}.
|
||||
${c2} .=+++++++++= :=+++++=
|
||||
.:::--- -+++++++-
|
||||
:-==++==-.
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
nixos_colorful = AsciiArt(match=r'''"nixos_colorful"*''', color='4 6 4 6 4 6', ascii=r"""
|
||||
${c1} ▗▄▄▄ ${c2}▗▄▄▄▄ ▄▄▄▖
|
||||
${c1} ▜███▙ ${c2}▜███▙ ▟███▛
|
||||
${c1} ▜███▙ ${c2}▜███▙▟███▛
|
||||
${c1} ▜███▙ ${c2}▜██████▛
|
||||
${c1} ▟█████████████████▙ ${c2}▜████▛ ${c3}▟▙
|
||||
${c1} ▟███████████████████▙ ${c2}▜███▙ ${c3}▟██▙
|
||||
${c6} ▄▄▄▄▖ ${c2}▜███▙ ${c3}▟███▛
|
||||
${c6} ▟███▛ ${c2}▜██▛ ${c3}▟███▛
|
||||
${c6} ▟███▛ ${c2}▜▛ ${c3}▟███▛
|
||||
${c6}▟███████████▛ ${c3}▟██████████▙
|
||||
${c6}▜██████████▛ ${c3}▟███████████▛
|
||||
${c6} ▟███▛ ${c5}▟▙ ${c3}▟███▛
|
||||
${c6} ▟███▛ ${c5}▟██▙ ${c3}▟███▛
|
||||
${c6} ▟███▛ ${c5}▜███▙ ${c3}▝▀▀▀▀
|
||||
${c6} ▜██▛ ${c5}▜███▙ ${c4}▜██████████████████▛
|
||||
${c6} ▜▛ ${c5}▟████▙ ${c4}▜████████████████▛
|
||||
${c5} ▟██████▙ ${c4}▜███▙
|
||||
${c5} ▟███▛▜███▙ ${c4}▜███▙
|
||||
${c5} ▟███▛ ▜███▙ ${c4}▜███▙
|
||||
${c5} ▝▀▀▀ ▀▀▀▀▘ ${c4}▀▀▀▘
|
||||
""")
|
||||
|
||||
@@ -3,21 +3,23 @@
|
||||
from . import AsciiArt
|
||||
|
||||
opensuse_leap = AsciiArt(match=r'''"openSUSE Leap"* | "openSUSE_Leap"*''', color='2 7', ascii=r"""
|
||||
${c2} `-++:`
|
||||
./oooooo/-
|
||||
`:oooooooooooo:.
|
||||
-+oooooooooooooooo+-`
|
||||
./oooooooooooooooooooooo/-
|
||||
:oooooooooooooooooooooooooo:
|
||||
` `-+oooooooooooooooooooo/- `
|
||||
`:oo/- .:ooooooooooooooo+:` `-+oo/.
|
||||
`/oooooo:. -/oooooooooo/. ./oooooo/.
|
||||
`:+ooooo+-` `:+oooo+- `:oooooo+:`
|
||||
.:oooooo/. .::` -+oooooo/.
|
||||
-/oooooo:. ./oooooo+-
|
||||
`:+ooooo+-:+oooooo:`
|
||||
./oooooooooo/.
|
||||
-/oooo+:`
|
||||
`:/.
|
||||
${c2} ====
|
||||
======
|
||||
==== ====+
|
||||
+==== +====
|
||||
+===+ ====
|
||||
==== ====
|
||||
+=== +====
|
||||
==== +====
|
||||
===== ====
|
||||
+===+ =====
|
||||
==+ ===== +===+ ===
|
||||
==== ==== ===== =====
|
||||
==== ======= ====
|
||||
==== === ====
|
||||
====+ ====
|
||||
==== =====
|
||||
======
|
||||
==
|
||||
""")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
opensuse_slowroll = AsciiArt(match=r'''"openSUSE 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?'
|
||||
""")
|
||||
|
||||
@@ -3,18 +3,27 @@
|
||||
from . import AsciiArt
|
||||
|
||||
opensuse_tumbleweed = AsciiArt(match=r'''"openSUSE Tumbleweed"* | "openSUSE_Tumbleweed"*''', color='2 7', ascii=r"""
|
||||
${c2} ......
|
||||
.,cdxxxoc,. .:kKMMMNWMMMNk:.
|
||||
cKMMN0OOOKWMMXo. A ;0MWk:' ':OMMk.
|
||||
;WMK;' 'lKMMNM, :NMK' 'OMW;
|
||||
cMW; WMMMN ,XMK' oMM.
|
||||
.MMc ''^*~l. xMN: KM0
|
||||
'MM. .NMO oMM
|
||||
.MM, .kMMl xMN
|
||||
KM0 .kMM0' .dl>~,. .WMd
|
||||
'XM0. ,OMMK' OMMM7' .XMK
|
||||
*WMO:. .;xNMMk' NNNMKl. .xWMx
|
||||
^ONMMNXMMMKx; V 'xNMWKkxllox0NMWk'
|
||||
''''' ':dOOXXKOxl'
|
||||
${c2} JJJJJJJJ
|
||||
JJJJJJJJJJJJJJ
|
||||
JJJJJJ =JJJJJJJ
|
||||
JJJJ =JJJ JJJJ
|
||||
JJJ =JJJ JJJ
|
||||
JJJJ =JJJ JJJ
|
||||
JJJJJJJJJJJJJJJ JJJJ
|
||||
JJJJJJJJJJJJJJ JJJJ
|
||||
JJJJ JJJJ
|
||||
JJJJJ= JJJJ
|
||||
JJJJJJJJJJJJJJJJJJJJJJJJJJJJJ=
|
||||
=JJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
|
||||
JJJJ =JJJJJJ
|
||||
JJJJ =JJJJ
|
||||
JJJJ JJJJJJJJJJJJJJ
|
||||
JJJJ JJJJJJJJJJJJJJJ
|
||||
JJJJ JJJJ JJJJ
|
||||
JJJ JJJJ JJJ
|
||||
JJJJJ JJJJ JJJJ
|
||||
=JJJJJJJJ JJJJJJ
|
||||
JJJJJJJJJJJJJJ
|
||||
JJJJJJJ=
|
||||
""")
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
openwrt = AsciiArt(match=r'''"OpenWrt"*''', color='4 7 1', ascii=r"""
|
||||
${c1} _______
|
||||
| |.-----.-----.-----.
|
||||
| - || _ | -__| |
|
||||
|_______|| __|_____|__|__|
|
||||
|__|
|
||||
________ __
|
||||
| | | |.----.| |_
|
||||
| | | || _|| _|
|
||||
|________||__| |____|
|
||||
""")
|
||||
|
||||
@@ -3,13 +3,34 @@
|
||||
from . import AsciiArt
|
||||
|
||||
pikaos = AsciiArt(match=r'''"PikaOS"*''', color='11', ascii=r"""
|
||||
${c1} /🐦🐦🐦$ /🐦/🐦 /🐦🐦🐦 /🐦🐦🐦
|
||||
| 🐦__ $|__| 🐦 /🐦__ 🐦/🐦__ 🐦
|
||||
| 🐦 \ 🐦/$| 🐦 /🐦 /🐦🐦🐦| 🐦 \ $| 🐦 \__/
|
||||
| 🐦🐦🐦$| $| 🐦 /🐦/|____ $| 🐦 | $| 🐦🐦🐦
|
||||
| 🐦____/| $| 🐦🐦🐦/ /🐦🐦🐦| 🐦 | 🐦\____ 🐦
|
||||
| 🐦 | $| 🐦_ 🐦 /🐦__ $| 🐦 | 🐦/🐦 \ 🐦
|
||||
| 🐦 | $| 🐦 \ $| 🐦🐦🐦| 🐦🐦🐦| 🐦🐦🐦/
|
||||
|__/ |__|__/ \__/\_______/\______/ \______/ .
|
||||
${c1}
|
||||
'',,, ,,,d,
|
||||
',, ,,'
|
||||
', ,.
|
||||
., '
|
||||
. .
|
||||
' .
|
||||
.. oddddkdlc:;,.. ..
|
||||
. ............lllc, .
|
||||
. ....................: .
|
||||
. . ....................
|
||||
'. ..........'o........d0XX0.
|
||||
....lllllllcOOOcllllll............cxlxc...;okkkx.
|
||||
..................................';lc'...lo.
|
||||
.'''''''''''''.....................,;,.......
|
||||
',,,,,,,,,,,,,,,,''...............dkkkd......
|
||||
',,,,,,,,,,,,,,,,,,,'............;okkkd;....
|
||||
.,,,,,,,,,,,,,,,,,,,,,............;cll;.....
|
||||
,,,,,,,,,,,,,,,,,,,,'....................:d,
|
||||
,,,,,,,,,,,,,,,,,,,....................oxxx:
|
||||
.,,,,,,,,,,,,,,,,,'..................oxxxxx. .
|
||||
.,,,,,,,,,,,,,,'.......... ,oxxxxxxx .
|
||||
.;,,,,,,,,,,,,'.. 'cxxxxxxxxx,
|
||||
:dl:,'',,'.... .;lxxxxxxxxxd;
|
||||
,codol:;'. ...,;cldxxxxxxxxxoc.
|
||||
.:cxxxxxdlccccc:ccldxxxxxxxxxxxxxx::.
|
||||
.'::dxxxxxxxxxxxxxxxxxxxxxxxd::'.
|
||||
..,::cdxxxxxxxxxxxxxdc::,..
|
||||
...,;:::::::;,...
|
||||
""")
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
puffos = AsciiArt(match=r'''"PuffOS"*''', color='3', ascii=r"""
|
||||
puffos = AsciiArt(match=r'''"PuffOS"*''', color='3', ascii=r'''
|
||||
${c1}
|
||||
_,..._,m,
|
||||
,/' '"";
|
||||
/ ".
|
||||
,'mmmMMMMmm. \
|
||||
_/-"^^^^^"\""%#%mm, ;
|
||||
_/-"^^^^^"""%#%mm, ;
|
||||
,m,_,' "###) ;,
|
||||
(###% \#/ ;##mm.
|
||||
^#/ __ ___ ; (######)
|
||||
@@ -16,5 +16,5 @@ ${c1}
|
||||
_; (#\"// \"/#) ; ,/
|
||||
@##\ \##/ = `"=" ,;mm/
|
||||
`\##>.____,...,____,<####@
|
||||
""")
|
||||
''')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
salix = AsciiArt(match=r'''"Salix"*''', color='2', ascii=r"""
|
||||
salix = AsciiArt(match=r'''"Salix"*''', color='2', ascii=r'''
|
||||
${c1} __s_aaaaaaaaauuoXSSSSSSSS:
|
||||
._xSSSSSSSSSSSSSSSSSSSSSSSSSS:
|
||||
_aSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:
|
||||
@@ -15,13 +15,13 @@ ${c1} __s_aaaaaaaaauuoXSSSSSSSS:
|
||||
oSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS;
|
||||
:XSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS;
|
||||
{SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
|
||||
-"\"\""\"\""\"\""\"\""\"\""\"\""\"\""\"\""\"\""\"\""\"\"\""'
|
||||
-"""""""""""""""""""""""""""""""""""'
|
||||
|
||||
<assssssssssssssssssssssssssssssss>
|
||||
nSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS}
|
||||
nSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS}`
|
||||
XSSSSSSSSSSSSSSSSSSSSSSSSSSSS"`
|
||||
SSSSSSSSSSSSSSSSSSSSSSSSS!"`
|
||||
-"\"\""\"\""\"\""\"\""\"\""\"\""\"\""`
|
||||
""")
|
||||
-""""""""""""""""""""""`
|
||||
''')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
skiffos = AsciiArt(match=r'''"SkiffOS"*''', color='4 7', ascii=r"""
|
||||
skiffos = AsciiArt(match=r'''"SkiffOS"*''', color='4 7', ascii=r'''
|
||||
${c2}
|
||||
,@@@@@@@@@@@w,_
|
||||
${c2}====~~~,,.${c2}A@@@@@@@@@@@@@@@@@W,_
|
||||
@@ -12,6 +12,6 @@ ${c2}
|
||||
${c2}*@@@@@@@@@@@@@@@@@@@@@@@@@p${c1}||||==,
|
||||
${c1}`'||LLL{{""${c2}@$B@@@@@@@@@@@@@@@p${c1}||
|
||||
${c1}`~=|||||||||||L"${c2}$@@@@@@@@@@@
|
||||
${c1}````'"\"\""\"\""${c2}'"\"\""\"\"\""
|
||||
""")
|
||||
${c1}````'"""""""${c2}'""""""""
|
||||
''')
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
slackel = AsciiArt(match=r'''"Slackel"*''', color='3', ascii=r"""
|
||||
slackel = AsciiArt(match=r'''"Slackel"*''', color='3', ascii=r'''
|
||||
${c1} _aawmmmmmwwaaaaas,,,_.
|
||||
.ammmmm###mmmmmmm###BQmm##mws
|
||||
.am###mmBmBmBmBmBmBmmmmm#mmmm#2
|
||||
<q###mmBmBmBmBmBmBmBmBmBmBmmBmZ`
|
||||
um#mmmBmBm##U##mmBmBmBmWmmBmWm#(
|
||||
.wm#mmBBmm#Y~ ~XmBmBmWmmmmmBm#e
|
||||
.dm#mmWmm#Z' ]#mBmBmmBZ!"\"\""`
|
||||
.dm#mmWmm#Z' ]#mBmBmmBZ!""""`
|
||||
.dm#mmBmm#2` ]mmmBmBm#2
|
||||
jm#mmWmm#2` dmmBmBmB#(
|
||||
)m##mBmmWZ` )##mBmBmmZ
|
||||
@@ -25,5 +25,5 @@ XBmBmBmBmm6s_aum##mmBmBm&^
|
||||
)ZmBmBmmmBmBmmBmB##!`
|
||||
-4U#mBmWmBmBm##2"
|
||||
-!!XU##US*?"-
|
||||
""")
|
||||
''')
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
tatra = AsciiArt(match=r'''"Tatra"*''', color='4 7', ascii=r"""
|
||||
${c1}
|
||||
. .:. . .:.
|
||||
.^^.!.:::. .^!?J?^
|
||||
.:^.^!!!~:~^. .7??77!~~^.
|
||||
.~^.!??77?!.^~: ..:^^7JJJ7~~^.
|
||||
.^~.^7???7~.~~. .7??????????!
|
||||
.:^:^^~^^:!^ ^: .......^!:...
|
||||
.!7~.::.!.::. ~BG~ :^ ^~:
|
||||
:!!~ ~. ?BBBB! ^?J!. .!~.
|
||||
:!. .JBY. .Y#BBBY?~!???J7. :^^.
|
||||
.. :5#B#P~P#BBP?7?55J?J7:
|
||||
^P#BBBBBBBB5?7J5555J!.....
|
||||
!BBBBBBGBBGJ77::Y555J?77777^
|
||||
?BBBBG5JJ5PJ?!: .?Y??????77?~.
|
||||
.YBGPYJ??????Y?^^^^~7?????????7?!.
|
||||
.^^:..::::::::.....:::::::::::..:.
|
||||
""")
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
trisquel = AsciiArt(match=r'''"Trisquel"*''', color='4 6', ascii=r"""
|
||||
trisquel = AsciiArt(match=r'''"Trisquel"*''', color='4 6', ascii=r'''
|
||||
${c1} ,o$$$o.
|
||||
,o$$Y"\""Y$$b
|
||||
,o$$Y"""Y$$b
|
||||
,o$$$$$$o. ,$$' , Y$$b
|
||||
,o$$$$$$$$$$$$o. :$ b Y$$.
|
||||
,$$"' "Y$$$$o. 'b. ,b d$$$
|
||||
@@ -12,7 +12,7 @@ $$' .d$$$$b '$$$$o 'Y$$$Y d$$$'
|
||||
$$' q' 'b '$$$$$o._ _.o$$$$'
|
||||
.$$,_ _,d$ ,$$$$$$$$$$$$${c2}$$$$Y'
|
||||
${c1} '$$$$aaa$$$' .$$$$$$${c2}$$$$$$$$'
|
||||
${c1} "\"\"" ${c2}d$$$$"'
|
||||
${c1} """" ${c2}d$$$$"'
|
||||
d$$$' .d$$b.
|
||||
$$$$ .$" 'a$.
|
||||
$$$$ $b $$.
|
||||
@@ -20,5 +20,5 @@ ${c1} "\"\"" ${c2}d$$$$"'
|
||||
'$$$. .$$'
|
||||
'a$$$o._.o$$a'
|
||||
'a$$$$a'
|
||||
""")
|
||||
''')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
windows95 = AsciiArt(match=r'''"Windows95"''', color='6 4 3 2 1 0', ascii=r"""
|
||||
windows95 = AsciiArt(match=r'''"Windows95"''', color='6 4 3 2 1 0', ascii=r'''
|
||||
${c6} ___
|
||||
.--=+++++=-:.
|
||||
. _ *%@@@@@@@@@@@@@@*
|
||||
@@ -18,8 +18,8 @@ ${c1} " , ${c6}%@@ ${c1}&&&&&%${c6}@@${c3}%&&&&&#${c6}@@
|
||||
${c1} * oo *# ${c6}" _ ${c1}&&&&&%${c6}@@${c3}%&&&&&#${c6}@@
|
||||
${c1} " , ${c6}%@@ ${c1}&&*"${c6}%@@@@@@${c3}"*%&${c6}@@
|
||||
.${c1} * oo *# ${c6}" _ @@@@@@@@@@@@@@@@@
|
||||
*:+:.__ :+# @@@ @%#=+"\"\""\""+==%#@
|
||||
*:+:.__ :+# @@@ @%#=+""""""+==%#@
|
||||
"+.-#+ +%* %+" " ":@
|
||||
" "
|
||||
""")
|
||||
''')
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from hyfetch.constants import CACHE_PATH
|
||||
from hyfetch.neofetch_util import get_distro_name
|
||||
|
||||
|
||||
def get_font_logo() -> str:
|
||||
cache = CACHE_PATH / 'font_logo_cache.txt'
|
||||
if cache.exists():
|
||||
return cache.read_text('utf-8')
|
||||
|
||||
font_logos: dict[str, str] = json.loads((Path(__file__).parent / 'data/font_logos.json').read_text('utf-8'))
|
||||
font_logos = {k.lower(): v for k, v in font_logos.items()}
|
||||
|
||||
# Get the distro
|
||||
distro = get_distro_name().lower()
|
||||
|
||||
# Find most likely distro by containing string
|
||||
for k in font_logos.keys():
|
||||
if k in distro:
|
||||
distro = k
|
||||
# If not found, try matching partial string (by splitting with " ")
|
||||
if not distro:
|
||||
for k in font_logos.keys():
|
||||
if any(x in distro for x in k.split(' ')):
|
||||
distro = k
|
||||
# If still not found, give up
|
||||
if not distro:
|
||||
raise ValueError(f'No font logo found for distro: {distro}. The supported logos are in https://github.com/Lukas-W/font-logos')
|
||||
|
||||
logo = font_logos[distro]
|
||||
cache.parent.mkdir(parents=True, exist_ok=True)
|
||||
cache.write_text(logo)
|
||||
|
||||
return logo
|
||||
|
||||
+46
-5
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import importlib.util
|
||||
import json
|
||||
import random
|
||||
import traceback
|
||||
@@ -13,6 +14,7 @@ from . import termenv, neofetch_util, pride_month
|
||||
from .color_scale import Scale
|
||||
from .color_util import clear_screen
|
||||
from .constants import *
|
||||
from .font_logo import get_font_logo
|
||||
from .models import Config
|
||||
from .neofetch_util import *
|
||||
from .presets import PRESETS
|
||||
@@ -209,12 +211,15 @@ def create_config() -> Config:
|
||||
return def_lightness
|
||||
|
||||
try:
|
||||
lightness = int(lightness[:-1]) / 100 if lightness.endswith('%') else float(lightness)
|
||||
if lightness.endswith('%') or int(lightness) > 1:
|
||||
lightness = int(lightness[:-1]) / 100 if lightness.endswith('%') else int(lightness) / 100
|
||||
else:
|
||||
lightness = float(lightness)
|
||||
assert 0 <= lightness <= 1
|
||||
return lightness
|
||||
|
||||
except Exception:
|
||||
printc('&cUnable to parse lightness value, please input it as a decimal or percentage (e.g. 0.5 or 50%)')
|
||||
printc('&cUnable to parse lightness value, please enter a lightness value such as 45%, .45, or 45')
|
||||
|
||||
lightness = select_lightness()
|
||||
_prs = _prs.set_light_dl(lightness, light_dark)
|
||||
@@ -243,7 +248,7 @@ def create_config() -> Config:
|
||||
|
||||
# Random color schemes
|
||||
pis = list(range(len(_prs.unique_colors().colors)))
|
||||
slots = list(set(re.findall('(?<=\\${c)[0-9](?=})', asc)))
|
||||
slots = list(set(map(int, re.findall('(?<=\\${c)[0-9](?=})', asc))))
|
||||
while len(pis) < len(slots):
|
||||
pis += pis
|
||||
perm = {p[:len(slots)] for p in permutations(pis)}
|
||||
@@ -286,9 +291,36 @@ def create_config() -> Config:
|
||||
|
||||
update_title('Color alignment', color_alignment)
|
||||
|
||||
##############################
|
||||
# 6. Select *fetch backend
|
||||
def select_backend():
|
||||
clear_screen(title)
|
||||
print_title_prompt('Select a *fetch backend')
|
||||
|
||||
# Check if fastfetch is installed
|
||||
ff_path = fastfetch_path()
|
||||
|
||||
# Check if qwqfetch is installed (if the qwqfetch module can be imported)
|
||||
has_qwqfetch = importlib.util.find_spec('qwqfetch') is not None
|
||||
|
||||
printc('- &bneofetch&r: Written in bash, &nbest compatibility&r on Unix systems')
|
||||
printc('- &bfastfetch&r: Written in C, &nbest performance&r ' +
|
||||
('&c(Not installed)' if ff_path is None else f'&a(Installed at {ff_path})'))
|
||||
printc('- &bqwqfetch&r: Pure python, &nminimal dependencies&r ' +
|
||||
('&c(Not installed)' if not has_qwqfetch else ''))
|
||||
print()
|
||||
|
||||
# Use fastfetch as the default backend if it is installed
|
||||
def_backend = 'neofetch' if ff_path is None else 'fastfetch'
|
||||
|
||||
return literal_input('Your choice?', ['neofetch', 'fastfetch', 'qwqfetch'], def_backend)
|
||||
|
||||
backend = select_backend()
|
||||
update_title('Selected backend', backend)
|
||||
|
||||
# Create config
|
||||
clear_screen(title)
|
||||
c = Config(preset, color_system, light_dark, lightness, color_alignment)
|
||||
c = Config(preset, color_system, light_dark, lightness, color_alignment, backend)
|
||||
|
||||
# Save config
|
||||
print()
|
||||
@@ -306,7 +338,7 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
|
||||
parser.add_argument('-c', '--config', action='store_true', help=color(f'Configure hyfetch'))
|
||||
parser.add_argument('-C', '--config-file', dest='config_file', default=CONFIG_PATH, help=f'Use another config file')
|
||||
parser.add_argument('-p', '--preset', help=f'Use preset', choices=list(PRESETS.keys()))
|
||||
parser.add_argument('-p', '--preset', help=f'Use preset', choices=list(PRESETS.keys()) + ['random'])
|
||||
parser.add_argument('-m', '--mode', help=f'Color mode', choices=['8bit', 'rgb'])
|
||||
parser.add_argument('-b', '--backend', help=f'Choose a *fetch backend', choices=['qwqfetch', 'neofetch', 'fastfetch', 'fastfetch-old'])
|
||||
parser.add_argument('--args', help=f'Additional arguments pass-through to backend')
|
||||
@@ -319,6 +351,7 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
|
||||
parser.add_argument('--distro', '--test-distro', dest='distro', help=f'Test for a specific distro')
|
||||
parser.add_argument('--ascii-file', help='Use a specific file for the ascii art')
|
||||
parser.add_argument('--print-font-logo', action='store_true', help='Print the Font Logo / Nerd Font icon of your distro and exit')
|
||||
|
||||
# Hidden debug arguments
|
||||
# --test-print: Print the ascii distro and exit
|
||||
@@ -363,6 +396,10 @@ def run():
|
||||
print(get_distro_ascii())
|
||||
return
|
||||
|
||||
if args.print_font_logo:
|
||||
print(get_font_logo())
|
||||
return
|
||||
|
||||
# Check if user provided alternative config path
|
||||
if not args.config_file == CONFIG_PATH:
|
||||
args.config_file = Path(os.path.abspath(args.config_file))
|
||||
@@ -403,6 +440,10 @@ def run():
|
||||
config.backend = args.backend
|
||||
if args.args:
|
||||
config.args = args.args
|
||||
|
||||
# Random preset
|
||||
if config.preset == 'random':
|
||||
config.preset = random.choice(list(PRESETS.keys()))
|
||||
|
||||
# Override global color mode
|
||||
GLOBAL_CFG.color_mode = config.mode
|
||||
|
||||
+63
-76
@@ -7,23 +7,21 @@ import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from subprocess import check_output
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Iterable
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from .color_util import color, printc
|
||||
from .constants import GLOBAL_CFG, MINGIT_URL, IS_WINDOWS
|
||||
from .constants import GLOBAL_CFG, IS_WINDOWS
|
||||
from .distros import distro_detector
|
||||
from .presets import ColorProfile
|
||||
from .serializer import from_dict
|
||||
from .types import BackendLiteral, ColorAlignMode
|
||||
|
||||
RE_NEOFETCH_COLOR = re.compile('\\${c[0-9]}')
|
||||
SRC = Path(__file__).parent
|
||||
|
||||
|
||||
def literal_input(prompt: str, options: Iterable[str], default: str, show_ops: bool = True) -> str:
|
||||
@@ -131,7 +129,16 @@ class ColorAlignment:
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
return from_dict(cls, d)
|
||||
ca = from_dict(cls, d)
|
||||
# Backward compatibility
|
||||
if type(ca.custom_colors) is not dict:
|
||||
if type(ca.custom_colors) is list:
|
||||
ca.custom_colors = {i + 1: v for i, v in enumerate(ca.custom_colors)}
|
||||
else:
|
||||
ca.custom_colors = {}
|
||||
# Fixup: Keys must json serialize as str, so we convert them back to int.
|
||||
ca.custom_colors = {int(k): v for k, v in ca.custom_colors.items()}
|
||||
return ca
|
||||
|
||||
def recolor_ascii(self, asc: str, preset: ColorProfile) -> str:
|
||||
"""
|
||||
@@ -197,24 +204,13 @@ def get_command_path() -> str:
|
||||
|
||||
:return: Command path
|
||||
"""
|
||||
cmd_path = pkg_resources.resource_filename(__name__, 'scripts/neowofetch')
|
||||
cmd_path = (if_file(SRC.parent / 'neofetch') or if_file(SRC / 'scripts/neowofetch'))
|
||||
|
||||
# Windows doesn't support symbolic links, but also I can't detect symbolic links... hard-code it here for now.
|
||||
if IS_WINDOWS:
|
||||
pkg = Path(__file__).parent
|
||||
pth = (shutil.which("neowofetch") or
|
||||
if_file(cmd_path) or
|
||||
if_file(pkg / 'scripts/neowofetch') or
|
||||
if_file(pkg.parent / 'neofetch') or
|
||||
if_file(Path(cmd_path).parent.parent.parent / 'neofetch'))
|
||||
if not cmd_path:
|
||||
printc("&cError: Neofetch script cannot be found")
|
||||
exit(127)
|
||||
|
||||
if not pth:
|
||||
printc("&cError: Neofetch script cannot be found")
|
||||
exit(127)
|
||||
|
||||
return str(pth)
|
||||
|
||||
return cmd_path
|
||||
return str(cmd_path)
|
||||
|
||||
|
||||
def ensure_git_bash() -> Path:
|
||||
@@ -223,46 +219,19 @@ def ensure_git_bash() -> Path:
|
||||
|
||||
:returns git bash path
|
||||
"""
|
||||
if IS_WINDOWS:
|
||||
# Find installation in default path
|
||||
def_path = Path(r'C:\Program Files\Git\bin\bash.exe')
|
||||
if def_path.is_file():
|
||||
return def_path
|
||||
if not IS_WINDOWS:
|
||||
return Path('/usr/bin/bash')
|
||||
|
||||
# Detect third-party git.exe in path
|
||||
git_exe = shutil.which("bash") or shutil.which("git.exe") or shutil.which("git")
|
||||
if git_exe is not None:
|
||||
pth = Path(git_exe).parent
|
||||
if (pth / r'bash.exe').is_file():
|
||||
return pth / r'bash.exe'
|
||||
elif (pth / r'bin\bash.exe').is_file():
|
||||
return pth / r'bin\bash.exe'
|
||||
# Bundled git bash
|
||||
git_path = (if_file(SRC / 'git/bin/bash.exe')
|
||||
or if_file("C:/Program Files/Git/bin/bash.exe")
|
||||
or if_file("C:/Program Files (x86)/Git/bin/bash.exe"))
|
||||
|
||||
# Find installation in PATH (C:\Program Files\Git\cmd should be in path)
|
||||
pth = (os.environ.get('PATH') or '').lower().split(';')
|
||||
pth = [p for p in pth if p.endswith(r'\git\cmd')]
|
||||
if pth:
|
||||
return Path(pth[0]).parent / r'bin\bash.exe'
|
||||
if not git_path.is_file():
|
||||
printc("&cError: Git Bash installation not found")
|
||||
sys.exit(127)
|
||||
|
||||
# Previously downloaded portable installation
|
||||
path = Path(__file__).parent / 'min_git'
|
||||
pkg_path = path / 'package.zip'
|
||||
if path.is_dir():
|
||||
return path / r'bin\bash.exe'
|
||||
|
||||
# No installation found, download a portable installation
|
||||
print('Git installation not found. Git is required to use HyFetch/neofetch on Windows')
|
||||
if literal_input('Would you like to install a minimal package for Git? (if no is selected colors almost certainly won\'t work)', ['yes', 'no'], 'yes', False) == 'yes':
|
||||
print('Downloading a minimal portable package for Git...')
|
||||
from urllib.request import urlretrieve
|
||||
urlretrieve(MINGIT_URL, pkg_path)
|
||||
print('Download finished! Extracting...')
|
||||
with zipfile.ZipFile(pkg_path, 'r') as zip_ref:
|
||||
zip_ref.extractall(path)
|
||||
print('Done!')
|
||||
return path / r'bin\bash.exe'
|
||||
else:
|
||||
sys.exit()
|
||||
return git_path
|
||||
|
||||
|
||||
def check_windows_cmd():
|
||||
@@ -270,14 +239,14 @@ def check_windows_cmd():
|
||||
Check if this script is running under cmd.exe. If so, launch an external window with git bash
|
||||
since cmd doesn't support RGB colors.
|
||||
"""
|
||||
if IS_WINDOWS:
|
||||
import psutil
|
||||
# TODO: This line does not correctly identify cmd prompts...
|
||||
if psutil.Process(os.getppid()).name().lower().strip() == 'cmd.exe':
|
||||
print("cmd.exe doesn't support RGB colors, restarting in MinTTY...")
|
||||
cmd = f'"{ensure_git_bash().parent.parent / "usr/bin/mintty.exe"}" -s 110,40 -e python -m hyfetch --ask-exit'
|
||||
os.system(cmd)
|
||||
sys.exit(0)
|
||||
# if IS_WINDOWS:
|
||||
# import psutil
|
||||
# # TODO: This line does not correctly identify cmd prompts...
|
||||
# if psutil.Process(os.getppid()).name().lower().strip() == 'cmd.exe':
|
||||
# print("cmd.exe doesn't support RGB colors, restarting in MinTTY...")
|
||||
# cmd = f'"{ensure_git_bash().parent.parent / "usr/bin/mintty.exe"}" -s 110,40 -e python -m hyfetch --ask-exit'
|
||||
# os.system(cmd)
|
||||
# sys.exit(0)
|
||||
|
||||
|
||||
def run_neofetch_cmd(args: str, pipe: bool = False) -> str | None:
|
||||
@@ -285,15 +254,16 @@ def run_neofetch_cmd(args: str, pipe: bool = False) -> str | None:
|
||||
Run neofetch command
|
||||
"""
|
||||
if platform.system() != 'Windows':
|
||||
full_cmd = ['/usr/bin/env', 'bash', get_command_path(), *shlex.split(args)]
|
||||
bash = ['/usr/bin/env', 'bash'] if Path('/usr/bin/env').is_file() else [shutil.which('bash')]
|
||||
full_cmd = [*bash, get_command_path(), *shlex.split(args)]
|
||||
|
||||
else:
|
||||
cmd = get_command_path().replace("\\", "/").replace("C:/", "/c/")
|
||||
args = args.replace('\\', '/').replace('C:/', '/c/')
|
||||
|
||||
full_cmd = [ensure_git_bash(), '-c', f"'{cmd}' {args}"]
|
||||
# print(full_cmd)
|
||||
full_cmd = [ensure_git_bash(), cmd, *shlex.split(args)]
|
||||
|
||||
full_cmd = [str(c) for c in full_cmd]
|
||||
if pipe:
|
||||
return check_output(full_cmd).decode().strip()
|
||||
else:
|
||||
@@ -351,10 +321,10 @@ def run(asc: str, backend: BackendLiteral, args: str = ''):
|
||||
|
||||
def run_qwqfetch(asc: str, args: str = ''):
|
||||
"""
|
||||
Run neofetch with colors
|
||||
Run qwqfetch with colors
|
||||
|
||||
:param preset: Color palette
|
||||
:param alignment: Color alignment settings
|
||||
:param asc: Ascii art
|
||||
:param args: Additional arguments to pass to qwqfetch
|
||||
"""
|
||||
asc = asc.replace('\\', '\\\\')
|
||||
|
||||
@@ -367,7 +337,8 @@ def run_qwqfetch(asc: str, args: str = ''):
|
||||
except ImportError as e: # module not found etc
|
||||
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
|
||||
raise e
|
||||
exit(127)
|
||||
|
||||
|
||||
def run_neofetch(asc: str, args: str = ''):
|
||||
"""
|
||||
@@ -383,7 +354,7 @@ def run_neofetch(asc: str, args: str = ''):
|
||||
with TemporaryDirectory() as tmp_dir:
|
||||
tmp_dir = Path(tmp_dir)
|
||||
path = tmp_dir / 'ascii.txt'
|
||||
path.write_text(asc)
|
||||
path.write_text(asc, 'utf-8')
|
||||
|
||||
# Call neofetch with the temp file
|
||||
if args:
|
||||
@@ -391,6 +362,13 @@ def run_neofetch(asc: str, args: str = ''):
|
||||
run_neofetch_cmd(f'--ascii --source {path.absolute()} --ascii-colors' + args)
|
||||
|
||||
|
||||
def fastfetch_path() -> Path | None:
|
||||
return (shutil.which('fastfetch')
|
||||
or if_file(SRC / 'fastfetch/usr/bin/fastfetch')
|
||||
or if_file(SRC / 'fastfetch/fastfetch')
|
||||
or if_file(SRC / 'fastfetch/fastfetch.exe'))
|
||||
|
||||
|
||||
def run_fastfetch(asc: str, args: str = '', legacy: bool = False):
|
||||
"""
|
||||
Run neofetch with colors
|
||||
@@ -399,14 +377,22 @@ def run_fastfetch(asc: str, args: str = '', legacy: bool = False):
|
||||
:param args: Additional arguments to pass to fastfetch
|
||||
:param legacy: Set true when using fastfetch < 1.8.0
|
||||
"""
|
||||
# Find fastfetch binary
|
||||
ff_path = fastfetch_path()
|
||||
|
||||
if not ff_path:
|
||||
printc("&cError: fastfetch binary is not found. Please install fastfetch first.")
|
||||
exit(127)
|
||||
|
||||
# Write temp file
|
||||
with TemporaryDirectory() as tmp_dir:
|
||||
tmp_dir = Path(tmp_dir)
|
||||
path = tmp_dir / 'ascii.txt'
|
||||
path.write_text(asc)
|
||||
path.write_text(asc, 'utf-8')
|
||||
|
||||
# Call fastfetch with the temp file
|
||||
proc = subprocess.run(['fastfetch', '--raw' if legacy else '--file-raw', path.absolute(), *shlex.split(args)])
|
||||
proc = subprocess.run([str(ff_path), '--raw' if legacy else '--file-raw',
|
||||
str(path.absolute()), *shlex.split(args)])
|
||||
if proc.returncode == 144:
|
||||
printc("&6Error code 144 detected: Please upgrade fastfetch to >=1.8.0 or use the 'fastfetch-old' backend")
|
||||
|
||||
@@ -440,5 +426,6 @@ fore_back = {
|
||||
'ubuntu-mate': (2, 1),
|
||||
'ubuntu-studio': (2, 1),
|
||||
'ubuntu-sway': (2, 1),
|
||||
'ultramarine': (2, 1),
|
||||
}
|
||||
|
||||
|
||||
+322
-7
@@ -189,6 +189,17 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#282828'
|
||||
]),
|
||||
|
||||
# xenogender sourced from https://commons.wikimedia.org/wiki/File:Xenogender_pride_flag.svg
|
||||
'xenogender': ColorProfile([
|
||||
'#FF6692',
|
||||
'#FF9A98',
|
||||
'#FFB883',
|
||||
'#FBFFA8',
|
||||
'#85BCFF',
|
||||
'#9D85FF',
|
||||
'#A510FF'
|
||||
]),
|
||||
|
||||
'agender': ColorProfile([
|
||||
'#000000',
|
||||
'#BABABA',
|
||||
@@ -499,7 +510,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#740194',
|
||||
]),
|
||||
|
||||
'autoromantic': ColorProfile([ # symbol interpreted
|
||||
'autoromantic': ColorProfile([ # symbol interpreted
|
||||
'#99D9EA',
|
||||
'#99D9EA',
|
||||
'#3DA542',
|
||||
@@ -519,6 +530,27 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#5276D4',
|
||||
]).with_weights([1, 1, 1, 1, 1, 5, 5, 5])),
|
||||
|
||||
# sourced from https://commons.wikimedia.org/wiki/File:Girlflux_Pride_Flag.jpg
|
||||
"girlflux": ColorProfile([
|
||||
"f9e6d7",
|
||||
"f2526c",
|
||||
"bf0311",
|
||||
"e9c587",
|
||||
"bf0311",
|
||||
"f2526c",
|
||||
"f9e6d7",
|
||||
]),
|
||||
|
||||
# sourced from https://www.deviantart.com/pride-flags/art/Genderflux-1-543925589
|
||||
"genderflux": ColorProfile([
|
||||
"f47694",
|
||||
"f2a2b9",
|
||||
"cecece",
|
||||
"7ce0f7",
|
||||
"3ecdf9",
|
||||
"fff48d",
|
||||
]),
|
||||
|
||||
"finsexual": ColorProfile([
|
||||
"#B18EDF",
|
||||
"#D7B1E2",
|
||||
@@ -526,7 +558,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
"#F39FCE",
|
||||
"#EA7BB3",
|
||||
]),
|
||||
|
||||
|
||||
'unlabeled1': ColorProfile([
|
||||
'#EAF8E4',
|
||||
'#FDFDFB',
|
||||
@@ -553,6 +585,16 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#FFF798',
|
||||
]),
|
||||
|
||||
'pangender.contrast': ColorProfile([
|
||||
'#ffe87f',
|
||||
'#fcbaa6',
|
||||
'#fbc9f3',
|
||||
'#FFFFFF',
|
||||
'#fbc9f3',
|
||||
'#fcbaa6',
|
||||
'#ffe87f',
|
||||
]),
|
||||
|
||||
'gendernonconforming1': ColorProfile(
|
||||
ColorProfile([
|
||||
'#50284d',
|
||||
@@ -563,7 +605,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#96467b',
|
||||
'#50284d'
|
||||
]).with_weights([
|
||||
4,1,1,1,1,1,4
|
||||
4, 1, 1, 1, 1, 1, 4
|
||||
])
|
||||
),
|
||||
|
||||
@@ -610,7 +652,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
]),
|
||||
|
||||
# gendervoid and related flags sourced from: https://gender.fandom.com/wiki/Gendervoid
|
||||
'gendervoid' : ColorProfile([
|
||||
'gendervoid': ColorProfile([
|
||||
"#081149",
|
||||
"#4B484B",
|
||||
"#000000",
|
||||
@@ -618,7 +660,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
"#081149"
|
||||
]),
|
||||
|
||||
'voidgirl' : ColorProfile([
|
||||
'voidgirl': ColorProfile([
|
||||
"#180827",
|
||||
"#7A5A8B",
|
||||
"#E09BED",
|
||||
@@ -626,7 +668,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
"#180827"
|
||||
]),
|
||||
|
||||
'voidboy' : ColorProfile([
|
||||
'voidboy': ColorProfile([
|
||||
"#0B130C",
|
||||
"#547655",
|
||||
"#66B969",
|
||||
@@ -635,12 +677,253 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
]),
|
||||
|
||||
# used https://twitter.com/foxbrained/status/1667621855518236674/photo/1 as source and colorpicked
|
||||
'nonhuman-unity' : ColorProfile([
|
||||
'nonhuman-unity': ColorProfile([
|
||||
"#177B49",
|
||||
"#FFFFFF",
|
||||
"#593C90"
|
||||
]),
|
||||
|
||||
# used https://pluralpedia.org/w/Plurality#/media/File:Plural-Flag-1.jpg as source and colorpicked
|
||||
'plural': ColorProfile([
|
||||
"#2D0625",
|
||||
"#543475",
|
||||
"#7675C3",
|
||||
"#89C7B0",
|
||||
"#F3EDBD",
|
||||
]),
|
||||
|
||||
# sampled from https://es.m.wikipedia.org/wiki/Archivo:Fraysexual_flag.jpg
|
||||
'fraysexual': ColorProfile([
|
||||
'#226CB5',
|
||||
'#94E7DD',
|
||||
'#FFFFFF',
|
||||
'#636363',
|
||||
]),
|
||||
|
||||
# Queer Subcultures
|
||||
# sourced from https://commons.wikimedia.org/wiki/File:Bear_Brotherhood_flag.svg
|
||||
'bear': ColorProfile([
|
||||
'#623804',
|
||||
'#D56300',
|
||||
'#FEDD63',
|
||||
'#FEE6B8',
|
||||
'#FFFFFF',
|
||||
'#555555',
|
||||
]),
|
||||
|
||||
# colorpicked from https://commons.wikimedia.org/wiki/File:Butch_Flag.png
|
||||
'butch': ColorProfile([
|
||||
'#D72800',
|
||||
'#F17623',
|
||||
'#FF9C56',
|
||||
'#FFFDF6',
|
||||
'#FFCE89',
|
||||
'#FEAF02',
|
||||
'#A37000',
|
||||
]),
|
||||
|
||||
# colorpicked from https://commons.wikimedia.org/wiki/File:Leather,_Latex,_and_BDSM_pride_-_Light.svg
|
||||
'leather': ColorProfile([
|
||||
'#000000',
|
||||
'#252580',
|
||||
'#000000',
|
||||
'#252580',
|
||||
'#FFFFFF',
|
||||
'#252580',
|
||||
'#000000',
|
||||
'#252580',
|
||||
'#000000',
|
||||
]),
|
||||
|
||||
# colorpicked from https://commons.wikimedia.org/wiki/File:Official_Otter_Pride_Flag_by_Bearbackgear.jpg
|
||||
'otter': ColorProfile([
|
||||
'#263881',
|
||||
'#5C9DC9',
|
||||
'#FFFFFF',
|
||||
'#3A291D',
|
||||
'#5C9DC9',
|
||||
'#263881',
|
||||
]),
|
||||
|
||||
# colorpicked from https://commons.wikimedia.org/wiki/File:Twink_Pride_Flag_(proposed).svg
|
||||
'twink': ColorProfile([
|
||||
'#FFB2FF',
|
||||
'#FFFFFF',
|
||||
'#FFFF81',
|
||||
]),
|
||||
|
||||
'kenochoric': ColorProfile([
|
||||
'#000000',
|
||||
'#2E1569',
|
||||
'#824DB7',
|
||||
'#C7A1D6',
|
||||
]),
|
||||
|
||||
'veldian': ColorProfile([
|
||||
'#D182A8',
|
||||
'#FAF6E0',
|
||||
'#69ACBE',
|
||||
'#5D448F',
|
||||
'#3A113E',
|
||||
]),
|
||||
|
||||
'solian': ColorProfile([
|
||||
'#FFF8ED',
|
||||
'#FFE7A8',
|
||||
'#F1B870',
|
||||
'#A56058',
|
||||
'#46281E',
|
||||
]),
|
||||
|
||||
'lunian': ColorProfile([
|
||||
'#2F0E62',
|
||||
'#6F41B1',
|
||||
'#889FDF',
|
||||
'#7DDFD5',
|
||||
'#D2F2E2',
|
||||
]),
|
||||
|
||||
# Start of Extras by Jaida Corvera
|
||||
# polyamorous flag colors pulled from https://polyamproud.com/flag
|
||||
'polyam': ColorProfile([
|
||||
"#FFFFFF",
|
||||
"#FCBF00",
|
||||
"#009FE3",
|
||||
"#E50051",
|
||||
"#340C46",
|
||||
]),
|
||||
|
||||
'sapphic': ColorProfile([
|
||||
"#FD8BA8",
|
||||
"#FBF2FF",
|
||||
"#C76BC5",
|
||||
"#FDD768",
|
||||
"#C76BC5",
|
||||
"#FBF2FF",
|
||||
"#FD8BA8"
|
||||
]),
|
||||
|
||||
'androgyne': ColorProfile([
|
||||
"#FE007F",
|
||||
"#9832FF",
|
||||
"#00B8E7",
|
||||
]),
|
||||
|
||||
'interprogress': ColorProfile([
|
||||
"#FFD800",
|
||||
"#7902AA",
|
||||
"#FFFFFF",
|
||||
"#FFAFC8",
|
||||
"#74D7EE",
|
||||
"#613915",
|
||||
"#000000",
|
||||
'#E50000',
|
||||
'#FF8D00',
|
||||
'#FFEE00',
|
||||
'#028121',
|
||||
'#004CFF',
|
||||
'#770088'
|
||||
]),
|
||||
|
||||
'progress': ColorProfile([
|
||||
"#FFFFFF",
|
||||
"#FFAFC8",
|
||||
"#74D7EE",
|
||||
"#613915",
|
||||
"#000000",
|
||||
'#E50000',
|
||||
'#FF8D00',
|
||||
'#FFEE00',
|
||||
'#028121',
|
||||
'#004CFF',
|
||||
'#770088'
|
||||
]),
|
||||
|
||||
'intersex': ColorProfile([
|
||||
"#FFD800",
|
||||
"#FFD800",
|
||||
"#7902AA",
|
||||
"#FFD800",
|
||||
"#FFD800"
|
||||
]),
|
||||
|
||||
'old-polyam': ColorProfile([
|
||||
"#0000FF",
|
||||
"#FF0000",
|
||||
"#FFFF00",
|
||||
"#FF0000",
|
||||
"#000000"
|
||||
]),
|
||||
|
||||
'equal-rights': ColorProfile([
|
||||
"#0000FF",
|
||||
"#0000FF",
|
||||
"#FFFF00",
|
||||
"#0000FF",
|
||||
"#0000FF",
|
||||
"#FFFF00",
|
||||
"#0000FF",
|
||||
"#0000FF"
|
||||
]),
|
||||
|
||||
'drag': ColorProfile([
|
||||
"#CC67FF",
|
||||
"#FFFFFF",
|
||||
"#FFA3E3",
|
||||
"#FFFFFF",
|
||||
"#3366FF"
|
||||
]),
|
||||
|
||||
# Pronoun Flags
|
||||
|
||||
'pronounfluid': ColorProfile([
|
||||
"#ffb3f9",
|
||||
"#ffffff",
|
||||
"#d1fdcb",
|
||||
"#c7b0ff",
|
||||
"#000000",
|
||||
"#b8ccff"
|
||||
]),
|
||||
|
||||
'pronounflux': ColorProfile([
|
||||
"#fdb3f8",
|
||||
"#b6ccfa",
|
||||
"#18ddd3",
|
||||
"#64ff89",
|
||||
"#ff7690",
|
||||
"#ffffff"
|
||||
]),
|
||||
|
||||
'exipronoun': ColorProfile([
|
||||
"#1c3d34",
|
||||
"#ffffff",
|
||||
"#321848",
|
||||
"#000000"
|
||||
]),
|
||||
|
||||
'neopronoun': ColorProfile([
|
||||
"#bcec64",
|
||||
"#ffffff",
|
||||
"#38077a"
|
||||
]),
|
||||
|
||||
'neofluid': ColorProfile([
|
||||
"#ffeca0",
|
||||
"#ffffff",
|
||||
"#ffeca0",
|
||||
"#38087a",
|
||||
"#bcec64"
|
||||
]),
|
||||
|
||||
'genderqueer': ColorProfile([
|
||||
"#b57edc",
|
||||
"#b57edc",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#4a8123",
|
||||
"#4a8123"
|
||||
]),
|
||||
|
||||
# Meme flags
|
||||
'beiyang': ColorProfile([
|
||||
'#DF1B12',
|
||||
@@ -657,4 +940,36 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#7D3829',
|
||||
'#F3A26A',
|
||||
]),
|
||||
|
||||
'throatlozenges': ColorProfile([
|
||||
"#2759DA",
|
||||
"#03940D",
|
||||
"#F5F100",
|
||||
"#F59B00",
|
||||
"#B71212"
|
||||
]),
|
||||
|
||||
# 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'
|
||||
])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
from . import main
|
||||
from .color_util import printc
|
||||
|
||||
def run_py():
|
||||
try:
|
||||
main.run()
|
||||
except KeyboardInterrupt:
|
||||
printc('&cThe program is interrupted by ^C, exiting...')
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_py()
|
||||
@@ -0,0 +1,24 @@
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from .color_util import printc
|
||||
from .py import run_py
|
||||
|
||||
|
||||
def run_rust():
|
||||
# Find the rust executable
|
||||
pd = Path(__file__).parent.joinpath('rust')
|
||||
pd = pd.joinpath('hyfetch.exe' if platform.system() == 'Windows' else 'hyfetch')
|
||||
if not pd.exists():
|
||||
printc('&cThe rust executable is not found, falling back to python...')
|
||||
run_py()
|
||||
return
|
||||
|
||||
# Run the rust executable, passing in all arguments
|
||||
subprocess.run([str(pd)] + sys.argv[1:])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_rust()
|
||||
@@ -6,8 +6,8 @@ _shtab_hyfetch_option_strings=('-h' '--help' '-c' '--config' '-C' '--config-file
|
||||
|
||||
|
||||
|
||||
_shtab_hyfetch__p_choices=('rainbow' 'transgender' 'nonbinary' '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' 'finsexual' 'unlabeled1' 'unlabeled2' 'pangender' 'gendernonconforming1' 'gendernonconforming2' 'femboy' 'tomboy' 'gendervoid' 'voidgirl' 'voidboy' 'beiyang' 'burger')
|
||||
_shtab_hyfetch___preset_choices=('rainbow' 'transgender' 'nonbinary' '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' 'finsexual' 'unlabeled1' 'unlabeled2' 'pangender' 'gendernonconforming1' 'gendernonconforming2' 'femboy' 'tomboy' 'gendervoid' 'voidgirl' 'voidboy' 'beiyang' 'burger')
|
||||
_shtab_hyfetch__p_choices=('rainbow' 'transgender' 'nonbinary' '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' 'finsexual' 'unlabeled1' 'unlabeled2' 'pangender' 'gendernonconforming1' 'gendernonconforming2' 'femboy' 'tomboy' 'gendervoid' 'voidgirl' 'voidboy' 'plural' 'beiyang' 'burger' 'caninekin')
|
||||
_shtab_hyfetch___preset_choices=('rainbow' 'transgender' 'nonbinary' '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' 'finsexual' 'unlabeled1' 'unlabeled2' 'pangender' 'gendernonconforming1' 'gendernonconforming2' 'femboy' 'tomboy' 'gendervoid' 'voidgirl' 'voidboy' 'plural' 'beiyang' 'burger' 'caninekin')
|
||||
_shtab_hyfetch__m_choices=('8bit' 'rgb')
|
||||
_shtab_hyfetch___mode_choices=('8bit' 'rgb')
|
||||
_shtab_hyfetch__b_choices=('neofetch' 'fastfetch' 'fastfetch-old')
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
complete hyfetch \
|
||||
'c/--/(mode test-distro preset config c-scale c-set-l config-file ascii-file version debug distro help backend)/' \
|
||||
'c/-/(m h V C p b c -)/' \
|
||||
'n/-p/(rainbow transgender nonbinary 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 finsexual unlabeled1 unlabeled2 pangender gendernonconforming1 gendernonconforming2 femboy tomboy gendervoid voidgirl voidboy beiyang burger)/' \
|
||||
'n/--preset/(rainbow transgender nonbinary 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 finsexual unlabeled1 unlabeled2 pangender gendernonconforming1 gendernonconforming2 femboy tomboy gendervoid voidgirl voidboy beiyang burger)/' \
|
||||
'n/-p/(rainbow transgender nonbinary 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 finsexual unlabeled1 unlabeled2 pangender gendernonconforming1 gendernonconforming2 femboy tomboy gendervoid voidgirl voidboy plural beiyang burger caninekin)/' \
|
||||
'n/--preset/(rainbow transgender nonbinary 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 finsexual unlabeled1 unlabeled2 pangender gendernonconforming1 gendernonconforming2 femboy tomboy gendervoid voidgirl voidboy plural beiyang burger caninekin)/' \
|
||||
'n/-m/(8bit rgb)/' \
|
||||
'n/--mode/(8bit rgb)/' \
|
||||
'n/-b/(neofetch fastfetch fastfetch-old)/' \
|
||||
|
||||
@@ -14,7 +14,7 @@ _shtab_hyfetch_options=(
|
||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||
{-c,--config}"[Configure hyfetch]"
|
||||
{-C,--config-file}"[Use another config file]:config_file:"
|
||||
{-p,--preset}"[Use preset]:preset:(rainbow transgender nonbinary 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 finsexual unlabeled1 unlabeled2 pangender gendernonconforming1 gendernonconforming2 femboy tomboy gendervoid voidgirl voidboy beiyang burger)"
|
||||
{-p,--preset}"[Use preset]:preset:(rainbow transgender nonbinary 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 finsexual unlabeled1 unlabeled2 pangender gendernonconforming1 gendernonconforming2 femboy tomboy gendervoid voidgirl voidboy plural beiyang burger caninekin)"
|
||||
{-m,--mode}"[Color mode]:mode:(8bit rgb)"
|
||||
{-b,--backend}"[Choose a \*fetch backend]:backend:(neofetch fastfetch fastfetch-old)"
|
||||
"--c-scale[Lighten colors by a multiplier]:scale:"
|
||||
|
||||
+6
-3
@@ -18,7 +18,8 @@ def unix_detect_ansi_mode() -> AnsiMode | None:
|
||||
|
||||
:return: Ansi mode
|
||||
"""
|
||||
if not sys.stdout.isatty():
|
||||
# Check isatty attribute exist before calling it
|
||||
if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty():
|
||||
return 'ansi'
|
||||
|
||||
term = os.environ.get('TERM')
|
||||
@@ -54,7 +55,7 @@ def windows_detect_ansi_mode() -> AnsiMode | None:
|
||||
|
||||
:return: Ansi mode
|
||||
"""
|
||||
if not sys.stdout.isatty():
|
||||
if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty():
|
||||
return 'ansi'
|
||||
|
||||
if os.environ.get("ConEmuANSI") == "ON":
|
||||
@@ -134,9 +135,11 @@ def unix_read_osc(seq: int) -> str:
|
||||
# Terminate with sequence terminator [\ or bell ^G
|
||||
if code.endswith('\x1b\\') or code.endswith('\a'):
|
||||
break
|
||||
signal.alarm(0)
|
||||
except IOError:
|
||||
pass
|
||||
finally:
|
||||
# Disable alarm
|
||||
signal.alarm(0)
|
||||
|
||||
# Reset terminal back to normal mode (previously set to raw mode)
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, settings)
|
||||
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
from typing_extensions import Literal
|
||||
try:
|
||||
from typing import Literal
|
||||
except ImportError:
|
||||
from typing_extensions import Literal
|
||||
|
||||
AnsiMode = Literal['default', 'ansi', '8bit', 'rgb']
|
||||
LightDark = Literal['light', 'dark']
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "neowofetch",
|
||||
"version": "1.4.11",
|
||||
"version": "1.99.0",
|
||||
"description": "Updated neofetch",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
edition = "2021"
|
||||
|
||||
# empty_item_single_line = true
|
||||
# error_on_line_overflow = true
|
||||
# format_code_in_doc_comments = true
|
||||
# format_strings = true
|
||||
# group_imports = "StdExternalCrate"
|
||||
# imports_granularity = "Module"
|
||||
# imports_layout = "Mixed"
|
||||
match_block_trailing_comma = true
|
||||
newline_style = "Unix"
|
||||
# normalize_comments = true
|
||||
# normalize_doc_attributes = true
|
||||
# overflow_delimited_expr = true
|
||||
# reorder_impl_items = true
|
||||
use_field_init_shorthand = true
|
||||
use_try_shorthand = true
|
||||
# wrap_comments = true
|
||||
@@ -6,8 +6,10 @@ from setuptools import setup, find_namespace_packages
|
||||
HERE = Path(__file__).parent
|
||||
|
||||
# Load version without importing it (see issue #192 if you are confused)
|
||||
for l in (HERE / 'hyfetch' / '__version__.py').read_text().strip().splitlines():
|
||||
exec(l)
|
||||
VERSION = [l for l in (HERE / "Cargo.toml").read_text('utf-8').splitlines() if l.startswith("version = ")]
|
||||
if len(VERSION) != 1:
|
||||
raise ValueError(f"Cannot determine version from Cargo.toml: {VERSION}")
|
||||
VERSION = VERSION[0].split('"')[1]
|
||||
|
||||
# The text of the README file
|
||||
README = (HERE / "README.md").read_text('utf-8')
|
||||
@@ -31,13 +33,15 @@ setup(
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
],
|
||||
packages=find_namespace_packages(),
|
||||
packages=find_namespace_packages(exclude=("tools", "tools.*")),
|
||||
package_data={'hyfetch': ['hyfetch/*']},
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
# Universal dependencies
|
||||
'setuptools', 'typing_extensions',
|
||||
'typing_extensions; python_version < "3.8"',
|
||||
|
||||
# Windows dependencies
|
||||
'psutil ; platform_system=="Windows"',
|
||||
@@ -45,7 +49,9 @@ setup(
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"hyfetch=hyfetch.main:run",
|
||||
"hyfetch.v1=hyfetch.__main__:run_py",
|
||||
"hyfetch.rs=hyfetch.__main__:run_rust",
|
||||
"hyfetch=hyfetch.__main__:run_rust",
|
||||
]
|
||||
},
|
||||
scripts=['hyfetch/scripts/neowofetch']
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
### Issue #261 analysis
|
||||
|
||||
For context, see https://github.com/hykilpikonna/hyfetch/issues/261
|
||||
|
||||
The files in this directory are related to the automated sentiment analysis for the Reddit comments.
|
||||
|
||||
* `reddit.js`: JS script to crawl relevant Reddit comments.
|
||||
* `reddit.json`: Crawled raw data
|
||||
* `reddit_gpt.py`: Python script categorizing comment sentiment using GPT-4o
|
||||
* `reddit_opinions.json`: Categorized sentiment data
|
||||
|
||||
These files are not really related to the functionality of hyfetch, but I'm pushing them here to
|
||||
make my analysis reproducible, and preserve the data in case the reddit post is deleted.
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
function trimSpaces(s) {
|
||||
return s.replaceAll("\n", "").replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
const cmts = $("shreddit-comment")
|
||||
const out = []
|
||||
// Parse comments from html
|
||||
cmts.each((i, cmt) => {
|
||||
const $cmt = $(cmt)
|
||||
// Author: slot="commentMeta" (use the first one)
|
||||
const author = trimSpaces($cmt.find("[slot=commentMeta]").first().text())
|
||||
// Content: slot="comment"
|
||||
const content = trimSpaces($cmt.find("[slot=comment]").first().text())
|
||||
// Upvotes: score attribute of shreddit-comment-action-row
|
||||
const upvotes = parseInt($cmt.find("shreddit-comment-action-row").attr("score"))
|
||||
out.push({ author, content, upvotes })
|
||||
})
|
||||
out
|
||||
@@ -0,0 +1,322 @@
|
||||
[
|
||||
{
|
||||
"author": "UNH0LYM0NK • 6d ago",
|
||||
"content": "Theres a flag?",
|
||||
"upvotes": 8
|
||||
},
|
||||
{
|
||||
"author": "spicy_capybara • 6d ago",
|
||||
"content": "Right? I’m not sure waving a flag for a disorder is really the way to go. Especially since HS is shared by all sexuality’s including straight.",
|
||||
"upvotes": 11
|
||||
},
|
||||
{
|
||||
"author": "textposts_only • 6d ago",
|
||||
"content": "Asexuality and transgender is also shared by all sexualities including straight",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 5d ago",
|
||||
"content": "Yeah but being transgender is a genderqueer identity and asexuals are also an uncommon sexuality. Being cis het is not queer.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "textposts_only • 5d ago",
|
||||
"content": "How is asexual a sexuality? Aces can still be straight or gay or what have you. There are varying degrees of asexuality. And i mentioned trans people because the poster above me did. It's simple, either we gatekeep the queer banner (stupid idea) or we let in people who are marginalized due to something they're either born with or identify with in regards to their sexuality or gender. I.e. aces, hypersexuality, queer ppl etc",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "Ah sex addiction is a queer identity. Got it.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "textposts_only • 4d ago",
|
||||
"content": "That's disrespectful.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "You said it not me. Also have you ever read the description of this subreddit. It refers to hypersexuality as sex addiction.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "textposts_only • 4d ago",
|
||||
"content": "If you boil anything down to it's essentials, however right or not, it sounds insulting. Asexuals? Since when is not wanting to Fuck a sexuality Transgender? Since when is being another gender than the one you're mistakenly assigned with a sexuality!",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago • Edited 4d ago",
|
||||
"content": "Bruh. Hypersexuality, sex addiction and compulsive sexual disorder are all used by the health professionals who deal with this shit. Imagine not understanding what asexuality is but using it to make a point. Imagine not understand that LGBT is for queer sexualities and genders.",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 6d ago • Edited 6d ago",
|
||||
"content": "Hs is not a gender identity or sexuality so idk why it would be part of lgbtq+. Hs is a mental disorder. There's definitely more acceptance and understanding needed and ppl need to judge it less but if you add it to a pride flag then you might as well add a flag for ocd or depression or 1000 other things that will eventually drown out the lgbtq stuff I wouldn't include it personally, it feels like the only reason they're even lumped in is because \"sexuality\" in in the name. It doesn't rrally have anything to do with lgbtq stuff imo",
|
||||
"upvotes": 9
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 6d ago",
|
||||
"content": "Pretty much every gender identity or sexuality was considered a mental disorder until it wasn't, tbf. Gay was considered a mental disorder, trans was considered a mental disorder. They weren't considered legit sexualities or identities. And before anyone says \"yes but hypersexuality is bad for you!\", like you don't think being a gay man in 1955 was bad for your physical and mental well-being? Gay is not a mental disorder, it's just how society treated it that made it detrimental to oneself.",
|
||||
"upvotes": 7
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 6d ago",
|
||||
"content": "Just what do you think hs is? The only negative that came from being gay were due to society. Society can change however it wants and the compulsive and self destructive behavior of hs will still be bad. Hs doesn't mean high libido",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 5d ago",
|
||||
"content": "People would have said the exact same thing about a myriad of different sexualities and identities before. They would have said that it's compulsive and self destructive and tons of other negative things. Hypersexuality is most harmful because acting out the needs you have is met with hostility by society. If you're a woman with an extremely high libido who seeks out multiple partners, more than what is \"normal\", you're seen as damaged goods and crazy. If you were hypersexual in a society that actually accepted sexuality and its needs, it wouldn't be nearly as bad as it can be now. And no, hs isn't just \"high libido\", it's more complicated than that, but like other identities it's something you can't change and which is made the worst in the experience by a puritan society that sees sexuality as suspicious.",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 5d ago",
|
||||
"content": "You can't agree that hs isn't just high libido and then talk about it as if it was just high libido. The problem with hs is that you feel COMPULSED to act on it despite any harm that it causes you. That harm can have nothing to do with society. Literally your entire comment talks about if we just accepted that people could fuck a lot, everything would be fine. You're the type of person that pretends hs is just high libido lol. You CAN change it! That's why people go into therapy and get medicated for. Like what are you even talking about. By telling people they can't change it you're trying to move them away from seeking help, why would you do that just to pretend like hs is a harmless personality quirk",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 5d ago",
|
||||
"content": "You CAN change it! That's why people go into therapy and get medicated for. Like what are you even talking about. Nah. You can repress it, but that's not the same as change it. There's no cure for being hypersexual, just as there isn't any cure for any sexuality. You can try and shove it down into silence with drugs and psychological methods, but that doesn't mean it's gone. It's pretty icky to me that you're like \"nooo you can't have a different perspective you must be pretending to be hs!\" I'd tell you about my own personal history and experiences and why I 100% know what I'm talking about, but not gonna divulge that in public. The harm that is caused by those compulsive feelings is very correlated to what society thinks and how it reacts to our actions. If you have an overwhelming compulsion to go out and do things that explicitly harm you, like puts your physical safety at serious risk, that's not just hypersexuality, that's hypersexuality plus something else, and mostly something else.",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 5d ago",
|
||||
"content": "Show me where I said you're pretending to have hs? You're pretending that it is equivalent to having high libido or that it's just like being gay. No amount of therapy or medication will stop you from being gay. There ARE effective treatments and therapy that can help people to deal with their compulsions and better manage them for the rest of their life. Pretending like there aren't because you want hs to be the same as being gay is just incredibly selfish.",
|
||||
"upvotes": -1
|
||||
},
|
||||
{
|
||||
"author": "[deleted] • 5d ago",
|
||||
"content": "So what do you think? They were correct back then and you can medicate yourself out of being gay? Or that they are wrong now and all the people here who are helped through therapy are just placebos? Just because they used to be wrong doesn't mean you never trust doctors again lol especially when I am alive because of the medication I got for hs",
|
||||
"upvotes": null
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 5d ago",
|
||||
"content": "So what do you think? They were correct back then and you can medicate yourself out of being gay? Or that they are wrong now and all the people here who are helped through therapy are just placebos? Just because they used to be wrong doesn't mean you never trust doctors again lol especially when I am alive because of the medication I got for hs",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "[deleted] • 5d ago",
|
||||
"content": "",
|
||||
"upvotes": null
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 5d ago",
|
||||
"content": "No amount of therapy or medication will stop you from being gay And no amount of therapy or medication will stop you from being hypersexual either. All it will do is dull you, it can't carve out a part of who you are. Drugs can tranquilize anyone down to the point where they are too numb to even feel, or they can impose themselves to disrupt your endocrine system, but at the end of the day, you are still you. With the drugs, now you're just buried beneath them. You're being very inconsiderate and rude with the implication that I \"want hs to be the same as being gay\". You know that's a disingenuous thing to say, because you know what I've been trying to say here and I am far from the first gal to say it. What makes hypersexuality so much worse is the same thing that has made other marginalized sexualities, such as being gay, much worse, which is our inheritance of a fundamentalist Christian world which viewed anything sexual as sinful and \"against God\". Barely 100 years ago, women were diagnosed (yes, literally clinically diagnosed by doctors and other professionals) as having being mentally ill with what they called \"hysteria\" because they had sexual desires or were inclined towards promiscuity. Women were and still are socially ostracized and mocked and bullied for the same thing still to this day. Note how that happened only to women, never to men. I really hope you do read this, and not just skim it so you can argue on the internet, because I think there's a good chance you've internalized the same fundamentalist religious and patriarchal viewpoints as I've just been describing. Sexuality is not a sickness. Being constantly horny is not a sickness. The only way it becomes a sickness is if you have strong compulsions to do things that put you in harms way, and you don't need hypersexuality to get you there either.",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 5d ago",
|
||||
"content": "Idk why you're trying to talk about the therapy when you clearly have no idea about it. There are coping mechanisms and mental strategies you can learn to deal with hs compulsions. There's medication that can help you without making you a zombie. I'd say look it up but since you will write off any modern medicine because 100 years ago they gave you coke for headaches ... what is the point? Also nice accusing me of being disingenuous and then writing everything I say off as just trying to win an online argument. I can tell you're super interested in a real conversation. Im happy that you cant fathom that hs comes with real mental problems that are not caused by society and will not be fixed by society. But hs is not equal to high libido and everything you said is just about people being judged for sleeping around a lot. Its just not what hs means. I can't link you the definition because you will just write it off so idk what else there is to talk about. I just think its disgusting to come to a hs support space and discourage people from seeking help by painting the available treatment as negative as you do and gaslighting people that all their issues are from society and that they shouldn't work to fix them.",
|
||||
"upvotes": 3
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 5d ago",
|
||||
"content": "Nah. I've gone to therapy a lot and it can be super useful. Maybe you should ask first instead of assuming? I also like drugs, both over the counter and uhh under it lol. Anyway, you're just really not listening, which is sad, because I really do think you've someone been convinced and probably still convince yourself that you're broken. Could've been nice to actually talk about this, but you didn't even address what I said at all about how society has made life shit for women and their sexuality both historically and contemporarily, which is actually a pretty important point that you should pay attention to, so idk ig there's just no way of getting through to you and having an actual conversation. I said many times that hs is not just high libido. Ik this, because I'm hs. All you do is assume and put words in my mouth. Ik the definition. No, I don't write off modern medicine. I'm a trans woman with over 4 years on HRT, trust me I love modern medicine lol. You really need to get better at listening to other people instead of just forming an unbreakable opinion based on your own worst-take guesswork.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 5d ago",
|
||||
"content": "Except the issue is that most people who struggle with hypersexuality is men and most men are not shamed for being sexual or watching a lot of porn or masturbating or having sex with multiple people. In fact society and people are generally overtly sexual and it's okay for them. Hypersexuality is not the same as being shamed for being gay and treated like an other. Hypersexuality is inward emotions coming from a compulsion that one cannot control themselves. I hate this comparison to actual lgbt identities. Like fuck off.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 5d ago",
|
||||
"content": "Except the issue is that most people who struggle with hypersexuality is men and most men are not shamed for being sexual or watching a lot of porn or masturbating or having sex with multiple people. In fact society and people are generally overtly sexual and it's okay for them. Uhh, yes, exactly? Meaning that how society treats us hugely changes how hypersexuality is experienced? It shows that as an example, it can be much harder for women because society doesn't accept it. This is really going over some of yalls heads lol.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 5d ago",
|
||||
"content": "The description for this subreddit refers to hypersexuality as sex addiction i.e sexual compulsion. You should do some research on the negative effects of sexual compulsion on people that is not entirely caused by society before you start speaking from a place of \"education\".",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 5d ago",
|
||||
"content": "I know what the negative effects of sexual compulsion are. Do you? Because most of those negative effects are negative as a consequence of societal rejection.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "Yeah sure cause feeling the need to compulsively masturbate when anxious is to do with societal rejection. Having uncontrollable fantasies are societal rejection. Feeling consumed by fantasies are societal rejection.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 4d ago",
|
||||
"content": "Masturbation is a common de-stress tool. Not just common, but harmless. Sexual fantasies are normal to have and yes, most people don't decide what they are, they're just there. Sounds more to me like you're dealing with the shame of having those desires and fantasies because it makes you feel like a freak, and that is because of societal norms of sexuality, yes.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago • Edited 4d ago",
|
||||
"content": "Except when It's compulsive and unwanted. If It's not compulsive and unwanted It's not hypersexuality.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 • 4d ago",
|
||||
"content": "Nobody controls when they get turned on. Obviously you can like \"schedule\" times when you want to deliberately turn yourself on ig, but arousal is usually spontaneous. I know hs isn't just \"you're horny a lot\". All I've been saying is that the condition of hypersexuality would be a lot better if society wasn't so puritanical. It makes the condition a lot worse because it makes people view themselves as freaks and compounds the condition with endless amounts of shame and self-loathing. If you can't understand that, there's nothing more to discuss.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Intercode001 • 5d ago",
|
||||
"content": "I feel like it's as much a \"sexuality\" as asexuality. Otherwise, what do you call asexuality? Also, some consider their hypersexuality to be an integral part of their identity, there's no question there. Language is what we make of it, that includes definitions, categories and usage.",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 5d ago",
|
||||
"content": "Asexuality only tells you which people the person feels sexually attracted to -> nobody. It doesn't include any crippling mental issues that require treatment or cause immense mental distress. It also doesn't mean no libido. Just as hs doesn't just mean high libido",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "Other-Bumblebee2769 • 5d ago",
|
||||
"content": "It's obe thing to have a place to chat about this stuff... waving a flag for this is cringe af",
|
||||
"upvotes": 3
|
||||
},
|
||||
{
|
||||
"author": "bitchisakarma • 6d ago",
|
||||
"content": "Every group doesn't need a flag",
|
||||
"upvotes": 4
|
||||
},
|
||||
{
|
||||
"author": "ArrogjentMan • 5d ago • Edited 5d ago",
|
||||
"content": "Hyper-sexuality is like any other obsession/ addiction - gaming, alcohol, drug use, gambling. But also very different because it involves the entire body too and not just parts of it + the mind. So yes it can be as bad as the other things mentioned above, if they get out of hand. It can be used as an identity flag only as much as how one would use mental illnesses - OCD, Autism, ADHD, PTSD, SA trauma survivor etc. It does not need to be a pride flag or an identity flag, because it is not a sexual orientation or an identity, it is a condition/ disorder. It becomes a pride thing only when it is not accepted. Then we are forced to spread awareness about it and stand up for it with pride.",
|
||||
"upvotes": 4
|
||||
},
|
||||
{
|
||||
"author": "Storm141 • 5d ago",
|
||||
"content": "Lol",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "polskialt • 6d ago",
|
||||
"content": "accepting this pride flag would be supportive or potentially harmful to your community honestly, and it hurts to say it, I worry that when hypersexual people hook up there's a risk of things getting out of control. obviously not all the time, but the risk is there. Maybe making it easier to do that isn't the safest idea.",
|
||||
"upvotes": 3
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 6d ago",
|
||||
"content": "Queer person here. Hypersexuality isn't a sexual or gender identity that's in the minority. It's a compulsive sexual behaviour that requires therapy or medical treatment. I think it would be disrespectful to queer identities to lump them in with mostly cis het people who struggle with being overly sexual in a way that effects their life.",
|
||||
"upvotes": 5
|
||||
},
|
||||
{
|
||||
"author": "ZookeepergameOne5236 • 6d ago",
|
||||
"content": "I agree. I'm bisexual and have been on Pride marches for the last cough years. Sexual and gender identity is different to compulsive and destructive behaviour. I view my HS the same way I do my alcoholism. A potentially destructive behaviour that I need to manage, not an aspect of my identity that is to be celebrated.",
|
||||
"upvotes": 6
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 6d ago",
|
||||
"content": "Exactly this! Like there has been arguments over what exactly HS is but with more and recent research, it's been agreed upon that it is a form of compulsive behaviour with only negative effects. It does irk me a lot to see a lot of people on this subreddit who act like it's just having a high libido that is solved by having a lot of sex.",
|
||||
"upvotes": 4
|
||||
},
|
||||
{
|
||||
"author": "DumpsterFireOfLove • 6d ago",
|
||||
"content": "It does irk me a lot to see a lot of people on this subreddit who act like it's just having a high libido that is solved by having a lot of sex. Respectfully, this is your view, not everyone’s. Some people struggle with it, some people have accepted it. Some people are in situations where it’s a problem that is concerning, some people aren’t. It is not a one size fits all experience.",
|
||||
"upvotes": -1
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 5d ago",
|
||||
"content": "Respectfully this is the view of medical professionals. If you do not have negative issues regarding your sexual behavior, that's a high sex drive not hypersexuality. Hypersexuality is also known as compulsive sexual behavior disorder, or more commonly, sex addiction. When a person has an obsessive fixation on sex, sexual acts, and sexual fantasies, they might be hypersexual. This fixation is typically so severe that it might disrupt a person’s daily functioning. Some research shows that up to 3% to 6% of people are living with some form of sexual addiction disorder or related disorders and that this condition predominantly affects men. A high sex drive is an increased sexual desire. On the other hand, hypersexuality involves out-of-control feelings and urges to have sex as well as high-frequency sexual behavior.",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower • 6d ago",
|
||||
"content": "Yeah this is a good way to put it",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "DumpsterFireOfLove • 6d ago",
|
||||
"content": "Bi HS person weighing in here: Um, I’d like a flag. But I understand the issues that many (including myself) HS people face struggling with/accepting their hypersexuality. So I think there’s a should be a healthy debate about it. Also, my two cents: if the ACE/Aro identities are increasingly becoming an accepted part of the LGBTQ+ community, why not HS?",
|
||||
"upvotes": 4
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 6d ago",
|
||||
"content": "Because hypersexuality is marked as compulsive sexual behavior that is only negative for their mental health by having unwanted sexual thoughts or engaging in unwanted sexual behaviour that a person cannot control or stop themselves from doing. It is not an identity. It also isn't inherently queer in that it doesn't say anything about who you are attracted to, only that you can't control your sexual behaviour or thoughts. Asexuality and aromantism are both an identity in that it communicate how one experiences or rather may not experience an attraction to others.",
|
||||
"upvotes": 3
|
||||
},
|
||||
{
|
||||
"author": "DumpsterFireOfLove • 6d ago",
|
||||
"content": "Yes, but you could also say the same for homosexuality, if we were having this debate back in the 70s. Homosexuality was classified as a mental illness in the DSM for a long time. And I take exception with what you’re saying about it only being a negative for someone’s mental health. Yes, it is a struggle, more so for some than for others, but yes. However, mostly a good part of that struggle is fitting into societies ideas of standard sexuality, just as many people struggled with their homosexual identity in the 70s, and still do today. If non-heterosexual identities were fully accepted in society, there would be no (or greatly lessened) struggle.",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 6d ago",
|
||||
"content": "Compulsive sexual behavior is sometimes called hypersexuality or sexual addiction. It's an intense focus on sexual fantasies, urges or behaviors that can't be controlled. This causes distress and problems for your health, job, relationships or other parts of your life. Compulsive sexual behavior may involve different kinds of commonly enjoyable sexual experiences. Examples include masturbation, sexual arousal by using a computer to communicate, multiple sexual partners, use of pornography or paying for sex. But when these sexual behaviors become a major, constant focus in your life, are difficult to control, cause problems in your life, or are harmful to you or others, that's likely compulsive sexual behavior. From the Mayo clinic. Symptoms Some signs that you may have compulsive sexual behavior include: You have repeated and intense sexual fantasies, urges, and behaviors that take up a lot of your time and feel as if they're beyond your control. You feel driven or have frequent urges to do certain sexual behaviors, feel a release of the tension afterward, but also feel guilt or deep regret. You've tried without success to reduce or control your sexual fantasies, urges or behavior. You use compulsive sexual behavior as an escape from other problems, such as loneliness, depression, anxiety or stress. You continue to engage in sexual behaviors in spite of them causing serious problems. These could include the possibility of getting or giving someone else a sexually transmitted infection, the loss of important relationships, trouble at work, financial issues, or legal problems. You have trouble making and keeping healthy and stable relationships. But sure this the same as being sexually attracted to the same gender or all genders or nobody at all.",
|
||||
"upvotes": 5
|
||||
},
|
||||
{
|
||||
"author": "DumpsterFireOfLove • 6d ago",
|
||||
"content": "Upvotes for the references, thank you. However, not all hypersexuality is considered compulsive sexual behavior. That’s my point. If your hypersexuality IS affecting your life negatively and is causing distress, that could be compulsive sexual behavior, and should be addressed. But if it’s NOT causing distress or problems in your life, then it is just another facet of who you are. Hypersexuality ≠ Compulsive Sexual Behavior.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "John___Coyote • 5d ago",
|
||||
"content": "Hypersexuality = Compulsive Sexual Behavior. If sexual behavior not compulsive then ≠ hypersexuality. intrusive sexual thoughts under self control ≠hypersexual This post ≠ an attack or insult.",
|
||||
"upvotes": 3
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 5d ago",
|
||||
"content": "All you're saying is you're not hypersexual and appropriating the term for your high sex drive/high libido. Kind of a shit thing to do and then turn around and try to soap box hypersexuality to people who actually suffer with it. Hypersexuality is in fact defined as compulsive sexual behaviour. The point is that it's normal sexual behaviour that is so perverse/time consuming/shame bringing that it ruins your mental health, physical health, personal relationships, work or education.",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "rockinvet02 • 6d ago",
|
||||
"content": "Are you going to include flags for all the mental health disorders or just this one?",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Bessini • 6d ago",
|
||||
"content": "Not everything needs a flag",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "loyalsubjectbeliever • 6d ago",
|
||||
"content": "I think it deserves to be there as much as asexuality. Neither is an orientation so I can see why people may not want it there but at the same time both deserve validation and understanding. After all, is asexuality is the lack of sexuality, why including it in a group of flags dedicated to sexual orientations? It just feels, to me, like excluding it means we're less valid than other groups. Hell, some orientations even have multiple names and flags.",
|
||||
"upvotes": -3
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 5d ago",
|
||||
"content": "Bruh. Imagine thinking a compulsive sexual disorder that causes real life issues is the same as a sexual identity ALSO asexuality is the lack of sexual attraction not sexuality and asexual people can still have a libido and enjoy sex. Maybe do a little bit more research before trying to compare the two.",
|
||||
"upvotes": -2
|
||||
},
|
||||
{
|
||||
"author": "loyalsubjectbeliever • 5d ago • Edited 5d ago",
|
||||
"content": "Imagine thinking that lack of sexual attraction is a sexual identity Not to mention that asexuality, homosexuality and transgender were considered illnesses too until they realized they weren't. Why are you being so aggressive?",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "That sounds like something a straight person would say.",
|
||||
"upvotes": -2
|
||||
},
|
||||
{
|
||||
"author": "loyalsubjectbeliever • 4d ago",
|
||||
"content": "Try again, I'm bi",
|
||||
"upvotes": 2
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "Damn, I thought a queer person would have been less queerphobic but here we are.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "loyalsubjectbeliever • 4d ago",
|
||||
"content": "I'm not queerphobic tho, and you're the one being aggressive as hell",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "Imagine thinking that lack of sexual attraction is a sexual identity Not being \"queerphobic\" while saying asexuality isn't a sexual identity.",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "loyalsubjectbeliever • 4d ago",
|
||||
"content": "It's lack thereof I have literally nothing against asexuals but way I see it asexuality and hypersexuality are two sides of the same medal. If one belongs then the other should too and vice versa. Now, can we agree to disagree so you can leave me alone and stop being aggressive?",
|
||||
"upvotes": 0
|
||||
},
|
||||
{
|
||||
"author": "Topperno • 4d ago",
|
||||
"content": "Hypersexuality describes a person's inability to control their sexual behavior, arousal, impulses, or urges to the point of causing distress in their personal, work, or school life. Healthcare professionals may also refer to hypersexuality as: compulsive sexual behavior disorder. excessive sexual behavior. Compulsive sexual behavior is sometimes called hypersexuality or sexual addiction. It's an intense focus on sexual fantasies, urges or behaviors that can't be controlled. This causes distress and problems for your health, job, relationships or other parts of your life. CSBD is characterized by repetitive, poorly controlled engagement in sexual impulses, urges, and behaviors (e.g., pornography use). For a diagnosis of CSBD, these sexual behaviors should result in clinically significant distress, neglect of responsibilities, interests, and health, and cause significant impairment in critical areas of functioning. Notably, people with CSBD derive little or no satisfaction from their sexual activities and often make unsuccessful efforts to reduce or stop their behavior. Compared to Asexual – A term used to describe someone who does not experience sexual attraction toward individuals of any gender. Asexuality is a sexual orientation, and is different from celibacy, in that celibacy is the choice to refrain from engaging in sexual behaviors and does not comment on one’s sexual attractions. An asexual individual may choose to engage in sexual behaviors for various reasons even while not experiencing sexual attraction. Asexuality is an identity and sexual orientation; it is not a medical condition. Sexual attraction is not necessary for a person to be healthy. Gray-A, gray-asexual, gray-sexual are terms used to describe individuals who feel as though their sexuality falls somewhere on the spectrum of sexuality between asexuality and sexuality. Demisexual individuals are those who do not experience primary sexual attraction but may experience secondary sexual attraction after a close emotional connection has already formed.",
|
||||
"upvotes": 1
|
||||
},
|
||||
{
|
||||
"author": "loyalsubjectbeliever • 4d ago",
|
||||
"content": "Or you know what? Even better, explain to me why it belongs in lgbtq. Because arguing without being open about the possibility of being wrong is stupid. Here's my point as to why if it belongs then so does hypersexuality and vice versa: When we think about, for example, a gay man, we're thinking of someone who doesn't simply feel sexually attracted to other men, there's romantic attraction too. It's not just about sexual desire. When it comes to asexuality or hypersexuality the attraction, romantic or sexual, is completely irrelevant. An asexual person and a hypersexual person can be either straight or not. They can be cis or not. I'm pretty confident about this last part because I have acquaintances of both types. You say asexuality belongs to lgbtq, OK. Then why wouldn't hypersexuality? In this thread some say that it's just a mental disorder and to that I have a couple of counter arguments, the first one being that some people were hypersexual from a very young age, before any trauma. I have a close friend like this, so in this case too I feel pretty confident in what I'm saying. The other counter argument is that asexuality was considered a disorder not too long ago too. Same with other categories that belong to the lgbtq community. So, please explain to me why you believe that I'm wrong. My point is not: asexuals don't belong to lgbtq PERIOD. My point is: if hypersexuality doesn't belong then neither should asexuality because they are two faces of the exact same medal.",
|
||||
"upvotes": 0
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,60 @@
|
||||
import json
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Read reddit data
|
||||
# data = json.loads(Path('reddit.json').read_text('utf-8'))
|
||||
data = json.loads(Path('reddit_opinions.json').read_text('utf-8'))
|
||||
|
||||
# Testing: Print all comments
|
||||
for cmt in data:
|
||||
print()
|
||||
print(f"{cmt['author']}")
|
||||
print(f"- {cmt['content']}")
|
||||
|
||||
# Loop through each comment, ask GPT-4o to generate a verdict on
|
||||
# whether this comment support or oppose adding the flag
|
||||
opinions = []
|
||||
for cmt in data:
|
||||
if 'opinion' in cmt:
|
||||
opinions.append(cmt)
|
||||
continue
|
||||
|
||||
out = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a data analyst asked to read reddit comments on a thread collecting "
|
||||
"opinions on adding the Hypersexual flag into a LGBT+ system information tool."},
|
||||
# One-shot learning example
|
||||
{"role": "user", "content": "Does the following comment support or oppose adding the flag? Please answer 'support' or 'oppose' or 'neutral'.\n\n"
|
||||
"Author: azaneko • 1d ago\nComment: I'd like a flag"},
|
||||
{"role": "system", "content": "Support"},
|
||||
# Asking the actual data
|
||||
{"role": "user", "content": "Does the following comment support or oppose adding the flag? Please answer 'support' or 'oppose' or 'neutral'.\n\n"
|
||||
f"Author: {cmt['author']}\nComment: {cmt['content']}"},
|
||||
],
|
||||
)
|
||||
|
||||
# Print the result
|
||||
res = out.choices[0].message.content.lower().strip()
|
||||
print("\n")
|
||||
print("Author:", cmt['author'])
|
||||
print("Comment:", cmt['content'])
|
||||
print("Opinion:", res)
|
||||
opinions.append({**cmt, 'opinion': res})
|
||||
Path('reddit_opinions.json').write_text(json.dumps(opinions, indent=2))
|
||||
|
||||
# Count opinions and upvotes
|
||||
counter = Counter()
|
||||
for cmt in opinions:
|
||||
counter[cmt['opinion']] += cmt.get('upvotes') or 0
|
||||
|
||||
print("\n")
|
||||
print("Opinions:")
|
||||
for k, v in counter.items():
|
||||
print(f"{k}: {v}")
|
||||
@@ -0,0 +1,164 @@
|
||||
[
|
||||
{
|
||||
"author": "UNH0LYM0NK \u2022 6d ago",
|
||||
"content": "Theres a flag?",
|
||||
"upvotes": 8,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "spicy_capybara \u2022 6d ago",
|
||||
"content": "Right? I\u2019m not sure waving a flag for a disorder is really the way to go. Especially since HS is shared by all sexuality\u2019s including straight.",
|
||||
"upvotes": 11,
|
||||
"opinion": "oppose"
|
||||
},
|
||||
{
|
||||
"author": "textposts_only \u2022 6d ago",
|
||||
"content": "Asexuality and transgender is also shared by all sexualities including straight",
|
||||
"upvotes": 1,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Topperno \u2022 5d ago",
|
||||
"content": "Yeah but being transgender is a genderqueer identity and asexuals are also an uncommon sexuality. Being cis het is not queer.",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "textposts_only \u2022 5d ago",
|
||||
"content": "How is asexual a sexuality? Aces can still be straight or gay or what have you. There are varying degrees of asexuality. And i mentioned trans people because the poster above me did. It's simple, either we gatekeep the queer banner (stupid idea) or we let in people who are marginalized due to something they're either born with or identify with in regards to their sexuality or gender. I.e. aces, hypersexuality, queer ppl etc",
|
||||
"upvotes": 1,
|
||||
"opinion": "support"
|
||||
},
|
||||
{
|
||||
"author": "Topperno \u2022 4d ago",
|
||||
"content": "Ah sex addiction is a queer identity. Got it.",
|
||||
"upvotes": 0,
|
||||
"opinion": "oppose"
|
||||
},
|
||||
{
|
||||
"author": "textposts_only \u2022 4d ago",
|
||||
"content": "That's disrespectful.",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Topperno \u2022 4d ago",
|
||||
"content": "You said it not me. Also have you ever read the description of this subreddit. It refers to hypersexuality as sex addiction.",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "textposts_only \u2022 4d ago",
|
||||
"content": "If you boil anything down to it's essentials, however right or not, it sounds insulting. Asexuals? Since when is not wanting to Fuck a sexuality Transgender? Since when is being another gender than the one you're mistakenly assigned with a sexuality!",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Topperno \u2022 4d ago \u2022 Edited 4d ago",
|
||||
"content": "Bruh. Hypersexuality, sex addiction and compulsive sexual disorder are all used by the health professionals who deal with this shit. Imagine not understanding what asexuality is but using it to make a point. Imagine not understand that LGBT is for queer sexualities and genders.",
|
||||
"upvotes": 1,
|
||||
"opinion": "support"
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower \u2022 6d ago \u2022 Edited 6d ago",
|
||||
"content": "Hs is not a gender identity or sexuality so idk why it would be part of lgbtq+. Hs is a mental disorder. There's definitely more acceptance and understanding needed and ppl need to judge it less but if you add it to a pride flag then you might as well add a flag for ocd or depression or 1000 other things that will eventually drown out the lgbtq stuff I wouldn't include it personally, it feels like the only reason they're even lumped in is because \"sexuality\" in in the name. It doesn't rrally have anything to do with lgbtq stuff imo",
|
||||
"upvotes": 9,
|
||||
"opinion": "oppose"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 6d ago",
|
||||
"content": "Pretty much every gender identity or sexuality was considered a mental disorder until it wasn't, tbf. Gay was considered a mental disorder, trans was considered a mental disorder. They weren't considered legit sexualities or identities. And before anyone says \"yes but hypersexuality is bad for you!\", like you don't think being a gay man in 1955 was bad for your physical and mental well-being? Gay is not a mental disorder, it's just how society treated it that made it detrimental to oneself.",
|
||||
"upvotes": 7,
|
||||
"opinion": "support"
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower \u2022 6d ago",
|
||||
"content": "Just what do you think hs is? The only negative that came from being gay were due to society. Society can change however it wants and the compulsive and self destructive behavior of hs will still be bad. Hs doesn't mean high libido",
|
||||
"upvotes": 2,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 5d ago",
|
||||
"content": "People would have said the exact same thing about a myriad of different sexualities and identities before. They would have said that it's compulsive and self destructive and tons of other negative things. Hypersexuality is most harmful because acting out the needs you have is met with hostility by society. If you're a woman with an extremely high libido who seeks out multiple partners, more than what is \"normal\", you're seen as damaged goods and crazy. If you were hypersexual in a society that actually accepted sexuality and its needs, it wouldn't be nearly as bad as it can be now. And no, hs isn't just \"high libido\", it's more complicated than that, but like other identities it's something you can't change and which is made the worst in the experience by a puritan society that sees sexuality as suspicious.",
|
||||
"upvotes": 2,
|
||||
"opinion": "support"
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower \u2022 5d ago",
|
||||
"content": "You can't agree that hs isn't just high libido and then talk about it as if it was just high libido. The problem with hs is that you feel COMPULSED to act on it despite any harm that it causes you. That harm can have nothing to do with society. Literally your entire comment talks about if we just accepted that people could fuck a lot, everything would be fine. You're the type of person that pretends hs is just high libido lol. You CAN change it! That's why people go into therapy and get medicated for. Like what are you even talking about. By telling people they can't change it you're trying to move them away from seeking help, why would you do that just to pretend like hs is a harmless personality quirk",
|
||||
"upvotes": 1,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 5d ago",
|
||||
"content": "You CAN change it! That's why people go into therapy and get medicated for. Like what are you even talking about. Nah. You can repress it, but that's not the same as change it. There's no cure for being hypersexual, just as there isn't any cure for any sexuality. You can try and shove it down into silence with drugs and psychological methods, but that doesn't mean it's gone. It's pretty icky to me that you're like \"nooo you can't have a different perspective you must be pretending to be hs!\" I'd tell you about my own personal history and experiences and why I 100% know what I'm talking about, but not gonna divulge that in public. The harm that is caused by those compulsive feelings is very correlated to what society thinks and how it reacts to our actions. If you have an overwhelming compulsion to go out and do things that explicitly harm you, like puts your physical safety at serious risk, that's not just hypersexuality, that's hypersexuality plus something else, and mostly something else.",
|
||||
"upvotes": 2,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower \u2022 5d ago",
|
||||
"content": "Show me where I said you're pretending to have hs? You're pretending that it is equivalent to having high libido or that it's just like being gay. No amount of therapy or medication will stop you from being gay. There ARE effective treatments and therapy that can help people to deal with their compulsions and better manage them for the rest of their life. Pretending like there aren't because you want hs to be the same as being gay is just incredibly selfish.",
|
||||
"upvotes": -1,
|
||||
"opinion": "oppose"
|
||||
},
|
||||
{
|
||||
"author": "[deleted] \u2022 5d ago",
|
||||
"content": "So what do you think? They were correct back then and you can medicate yourself out of being gay? Or that they are wrong now and all the people here who are helped through therapy are just placebos? Just because they used to be wrong doesn't mean you never trust doctors again lol especially when I am alive because of the medication I got for hs",
|
||||
"upvotes": null,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower \u2022 5d ago",
|
||||
"content": "So what do you think? They were correct back then and you can medicate yourself out of being gay? Or that they are wrong now and all the people here who are helped through therapy are just placebos? Just because they used to be wrong doesn't mean you never trust doctors again lol especially when I am alive because of the medication I got for hs",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "[deleted] \u2022 5d ago",
|
||||
"content": "",
|
||||
"upvotes": null,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 5d ago",
|
||||
"content": "No amount of therapy or medication will stop you from being gay And no amount of therapy or medication will stop you from being hypersexual either. All it will do is dull you, it can't carve out a part of who you are. Drugs can tranquilize anyone down to the point where they are too numb to even feel, or they can impose themselves to disrupt your endocrine system, but at the end of the day, you are still you. With the drugs, now you're just buried beneath them. You're being very inconsiderate and rude with the implication that I \"want hs to be the same as being gay\". You know that's a disingenuous thing to say, because you know what I've been trying to say here and I am far from the first gal to say it. What makes hypersexuality so much worse is the same thing that has made other marginalized sexualities, such as being gay, much worse, which is our inheritance of a fundamentalist Christian world which viewed anything sexual as sinful and \"against God\". Barely 100 years ago, women were diagnosed (yes, literally clinically diagnosed by doctors and other professionals) as having being mentally ill with what they called \"hysteria\" because they had sexual desires or were inclined towards promiscuity. Women were and still are socially ostracized and mocked and bullied for the same thing still to this day. Note how that happened only to women, never to men. I really hope you do read this, and not just skim it so you can argue on the internet, because I think there's a good chance you've internalized the same fundamentalist religious and patriarchal viewpoints as I've just been describing. Sexuality is not a sickness. Being constantly horny is not a sickness. The only way it becomes a sickness is if you have strong compulsions to do things that put you in harms way, and you don't need hypersexuality to get you there either.",
|
||||
"upvotes": 1,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Lillian_the_flower \u2022 5d ago",
|
||||
"content": "Idk why you're trying to talk about the therapy when you clearly have no idea about it. There are coping mechanisms and mental strategies you can learn to deal with hs compulsions. There's medication that can help you without making you a zombie. I'd say look it up but since you will write off any modern medicine because 100 years ago they gave you coke for headaches ... what is the point? Also nice accusing me of being disingenuous and then writing everything I say off as just trying to win an online argument. I can tell you're super interested in a real conversation. Im happy that you cant fathom that hs comes with real mental problems that are not caused by society and will not be fixed by society. But hs is not equal to high libido and everything you said is just about people being judged for sleeping around a lot. Its just not what hs means. I can't link you the definition because you will just write it off so idk what else there is to talk about. I just think its disgusting to come to a hs support space and discourage people from seeking help by painting the available treatment as negative as you do and gaslighting people that all their issues are from society and that they shouldn't work to fix them.",
|
||||
"upvotes": 3,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 5d ago",
|
||||
"content": "Nah. I've gone to therapy a lot and it can be super useful. Maybe you should ask first instead of assuming? I also like drugs, both over the counter and uhh under it lol. Anyway, you're just really not listening, which is sad, because I really do think you've someone been convinced and probably still convince yourself that you're broken. Could've been nice to actually talk about this, but you didn't even address what I said at all about how society has made life shit for women and their sexuality both historically and contemporarily, which is actually a pretty important point that you should pay attention to, so idk ig there's just no way of getting through to you and having an actual conversation. I said many times that hs is not just high libido. Ik this, because I'm hs. All you do is assume and put words in my mouth. Ik the definition. No, I don't write off modern medicine. I'm a trans woman with over 4 years on HRT, trust me I love modern medicine lol. You really need to get better at listening to other people instead of just forming an unbreakable opinion based on your own worst-take guesswork.",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Topperno \u2022 5d ago",
|
||||
"content": "Except the issue is that most people who struggle with hypersexuality is men and most men are not shamed for being sexual or watching a lot of porn or masturbating or having sex with multiple people. In fact society and people are generally overtly sexual and it's okay for them. Hypersexuality is not the same as being shamed for being gay and treated like an other. Hypersexuality is inward emotions coming from a compulsion that one cannot control themselves. I hate this comparison to actual lgbt identities. Like fuck off.",
|
||||
"upvotes": 0,
|
||||
"opinion": "oppose"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 5d ago",
|
||||
"content": "Except the issue is that most people who struggle with hypersexuality is men and most men are not shamed for being sexual or watching a lot of porn or masturbating or having sex with multiple people. In fact society and people are generally overtly sexual and it's okay for them. Uhh, yes, exactly? Meaning that how society treats us hugely changes how hypersexuality is experienced? It shows that as an example, it can be much harder for women because society doesn't accept it. This is really going over some of yalls heads lol.",
|
||||
"upvotes": 0,
|
||||
"opinion": "support"
|
||||
},
|
||||
{
|
||||
"author": "Topperno \u2022 5d ago",
|
||||
"content": "The description for this subreddit refers to hypersexuality as sex addiction i.e sexual compulsion. You should do some research on the negative effects of sexual compulsion on people that is not entirely caused by society before you start speaking from a place of \"education\".",
|
||||
"upvotes": 0,
|
||||
"opinion": "neutral"
|
||||
},
|
||||
{
|
||||
"author": "Key_Computer_4348 \u2022 5d ago",
|
||||
"content": "I know what the negative effects of sexual compulsion are. Do you? Because most of those negative effects are negative as a consequence of societal rejection.",
|
||||
"upvotes": 0,
|
||||
"opinion": "support"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,365 @@
|
||||
git-bash.exe
|
||||
git-cmd.exe
|
||||
cmd
|
||||
tmp
|
||||
dev
|
||||
bin/git.exe
|
||||
bin/sh.exe
|
||||
usr/share
|
||||
usr/ssl
|
||||
usr/libexec
|
||||
usr/lib
|
||||
usr/bin/git*
|
||||
usr/bin/*vim*
|
||||
usr/bin/*ssl*
|
||||
usr/bin/*perl*
|
||||
usr/bin/*gpg*
|
||||
usr/bin/*ssh*
|
||||
usr/bin/*crypto*
|
||||
usr/bin/*view*
|
||||
usr/bin/*svn*
|
||||
usr/bin/ex*.exe
|
||||
mingw32/share
|
||||
mingw32/lib
|
||||
mingw32/etc
|
||||
mingw32/doc
|
||||
mingw32/libexec
|
||||
mingw32/bin/git*
|
||||
mingw32/bin/Git*
|
||||
mingw32/bin/Avalonia*
|
||||
mingw32/bin/xz*
|
||||
mingw32/bin/*ssl*
|
||||
mingw32/bin/scalar.exe
|
||||
mingw32/bin/libSkiaSharp.dll
|
||||
mingw32/bin/*crypto*
|
||||
mingw32/bin/*Microsoft.Identity*
|
||||
mingw32/bin/*bz*
|
||||
mingw32/bin/tcl*
|
||||
mingw32/bin/*brotli*
|
||||
mingw32/bin/av*
|
||||
mingw32/bin/pdftotext.exe
|
||||
mingw32/bin/libiconv-2.dll
|
||||
mingw32/bin/libHarfBuzzSharp.dll
|
||||
mingw32/bin/SkiaSharp.dll
|
||||
mingw32/bin/libcurl-4.dll
|
||||
mingw32/bin/libgmp-10.dll
|
||||
mingw32/bin/System.Text.Json.dll
|
||||
mingw32/bin/gcmcore.dll
|
||||
mingw32/bin/curl.exe
|
||||
|
||||
# Automatically determined by access time
|
||||
usr/bin/rebase.exe
|
||||
usr/bin/rebaseall
|
||||
usr/bin/start
|
||||
usr/bin/column.exe
|
||||
usr/bin/dash.exe
|
||||
usr/bin/getopt.exe
|
||||
usr/bin/znew
|
||||
usr/bin/zless
|
||||
usr/bin/zipinfo.exe
|
||||
usr/bin/zipgrep
|
||||
usr/bin/zgrep
|
||||
usr/bin/zforce
|
||||
usr/bin/zfgrep
|
||||
usr/bin/zegrep
|
||||
usr/bin/zdiff
|
||||
usr/bin/zcmp
|
||||
usr/bin/zcat
|
||||
usr/bin/yes.exe
|
||||
usr/bin/yat2m.exe
|
||||
usr/bin/xxd.exe
|
||||
usr/bin/xgettext.exe
|
||||
usr/bin/xargs.exe
|
||||
usr/bin/winpty.exe
|
||||
usr/bin/wordpad
|
||||
usr/bin/winpty.dll
|
||||
usr/bin/winpty-debugserver.exe
|
||||
usr/bin/winpty-agent.exe
|
||||
usr/bin/whoami.exe
|
||||
usr/bin/who.exe
|
||||
usr/bin/which.exe
|
||||
usr/bin/watchgnupg.exe
|
||||
usr/bin/vi
|
||||
usr/bin/vdir.exe
|
||||
usr/bin/users.exe
|
||||
usr/bin/updatedb
|
||||
usr/bin/update-ca-trust
|
||||
usr/bin/unzipsfx.exe
|
||||
usr/bin/unzip.exe
|
||||
usr/bin/unlink.exe
|
||||
usr/bin/unix2mac.exe
|
||||
usr/bin/unix2dos.exe
|
||||
usr/bin/unexpand.exe
|
||||
usr/bin/uncompress
|
||||
usr/bin/umount.exe
|
||||
usr/bin/u2d.exe
|
||||
usr/bin/tzset.exe
|
||||
usr/bin/tty.exe
|
||||
usr/bin/tsort.exe
|
||||
usr/bin/tset.exe
|
||||
usr/bin/trust.exe
|
||||
usr/bin/truncate.exe
|
||||
usr/bin/true.exe
|
||||
usr/bin/tr.exe
|
||||
usr/bin/tput.exe
|
||||
usr/bin/touch.exe
|
||||
usr/bin/toe.exe
|
||||
usr/bin/timeout.exe
|
||||
usr/bin/tig.exe
|
||||
usr/bin/tic.exe
|
||||
usr/bin/test.exe
|
||||
usr/bin/tee.exe
|
||||
usr/bin/tar.exe
|
||||
usr/bin/tail.exe
|
||||
usr/bin/tac.exe
|
||||
usr/bin/tabs.exe
|
||||
usr/bin/sync.exe
|
||||
usr/bin/sum.exe
|
||||
usr/bin/stty.exe
|
||||
usr/bin/strace.exe
|
||||
usr/bin/stat.exe
|
||||
usr/bin/ssp.exe
|
||||
usr/bin/split.exe
|
||||
usr/bin/sleep.exe
|
||||
usr/bin/shuf.exe
|
||||
usr/bin/shred.exe
|
||||
usr/bin/sha512sum.exe
|
||||
usr/bin/sha384sum.exe
|
||||
usr/bin/sha256sum.exe
|
||||
usr/bin/sha224sum.exe
|
||||
usr/bin/sha1sum.exe
|
||||
usr/bin/sftp.exe
|
||||
usr/bin/sexp-conv.exe
|
||||
usr/bin/setmetamode.exe
|
||||
usr/bin/setfacl.exe
|
||||
usr/bin/seq.exe
|
||||
usr/bin/sdiff.exe
|
||||
usr/bin/scp.exe
|
||||
usr/bin/runcon.exe
|
||||
usr/bin/rnano.exe
|
||||
usr/bin/rmdir.exe
|
||||
usr/bin/rm.exe
|
||||
usr/bin/restore
|
||||
usr/bin/reset.exe
|
||||
usr/bin/regtool.exe
|
||||
usr/bin/recode-sr-latin.exe
|
||||
usr/bin/realpath.exe
|
||||
usr/bin/readlink.exe
|
||||
usr/bin/pwd.exe
|
||||
usr/bin/ptx.exe
|
||||
usr/bin/psl.exe
|
||||
usr/bin/psl-make-dafsa
|
||||
usr/bin/ps.exe
|
||||
usr/bin/profiler.exe
|
||||
usr/bin/printf.exe
|
||||
usr/bin/printenv.exe
|
||||
usr/bin/pr.exe
|
||||
usr/bin/pldd.exe
|
||||
usr/bin/pkcs1-conv.exe
|
||||
usr/bin/pinky.exe
|
||||
usr/bin/pinentry.exe
|
||||
usr/bin/pinentry-w32.exe
|
||||
usr/bin/pathchk.exe
|
||||
usr/bin/patch.exe
|
||||
usr/bin/paste.exe
|
||||
usr/bin/passwd.exe
|
||||
usr/bin/p11-kit.exe
|
||||
usr/bin/od.exe
|
||||
usr/bin/numfmt.exe
|
||||
usr/bin/nproc.exe
|
||||
usr/bin/notepad
|
||||
usr/bin/nohup.exe
|
||||
usr/bin/nl.exe
|
||||
usr/bin/nice.exe
|
||||
usr/bin/ngettext.exe
|
||||
usr/bin/nettle-pbkdf2.exe
|
||||
usr/bin/nettle-lfib-stream.exe
|
||||
usr/bin/nettle-hash.exe
|
||||
usr/bin/nano.exe
|
||||
usr/bin/mv.exe
|
||||
usr/bin/msys-z.dll
|
||||
usr/bin/msys-wind-0.dll
|
||||
usr/bin/msys-unistring-2.dll
|
||||
usr/bin/msys-ticw6.dll
|
||||
usr/bin/msys-tasn1-6.dll
|
||||
usr/bin/msys-sqlite3-0.dll
|
||||
usr/bin/msys-smartcols-1.dll
|
||||
usr/bin/msys-serf-1-0.dll
|
||||
usr/bin/msys-sasl2-3.dll
|
||||
usr/bin/msys-roken-18.dll
|
||||
usr/bin/msys-psl-5.dll
|
||||
usr/bin/msys-pcre2-posix-3.dll
|
||||
usr/bin/msys-pcre2-8-0.dll
|
||||
usr/bin/msys-p11-kit-0.dll
|
||||
usr/bin/msys-npth-0.dll
|
||||
usr/bin/msys-nettle-8.dll
|
||||
usr/bin/msys-magic-1.dll
|
||||
usr/bin/msys-lz4-1.dll
|
||||
usr/bin/msys-ksba-8.dll
|
||||
usr/bin/msys-krb5-26.dll
|
||||
usr/bin/msys-kafs-0.dll
|
||||
usr/bin/msys-idn2-0.dll
|
||||
usr/bin/msys-hx509-5.dll
|
||||
usr/bin/msys-hogweed-6.dll
|
||||
usr/bin/msys-heimntlm-0.dll
|
||||
usr/bin/msys-heimbase-1.dll
|
||||
usr/bin/msys-gssapi-3.dll
|
||||
usr/bin/msys-gobject-2.0-0.dll
|
||||
usr/bin/msys-gnutls-30.dll
|
||||
usr/bin/msys-gmodule-2.0-0.dll
|
||||
usr/bin/msys-glib-2.0-0.dll
|
||||
usr/bin/msys-gio-2.0-0.dll
|
||||
usr/bin/msys-gettextsrc-0-19-8-1.dll
|
||||
usr/bin/msys-gettextlib-0-19-8-1.dll
|
||||
usr/bin/msys-gcrypt-20.dll
|
||||
usr/bin/msys-fido2-1.dll
|
||||
usr/bin/msys-ffi-7.dll
|
||||
usr/bin/msys-expat-1.dll
|
||||
usr/bin/msys-edit-0.dll
|
||||
usr/bin/msys-crypt-0.dll
|
||||
usr/bin/msys-com_err-1.dll
|
||||
usr/bin/msys-cbor-0.11.dll
|
||||
usr/bin/msys-bz2-1.dll
|
||||
usr/bin/msys-assuan-0.dll
|
||||
usr/bin/msys-asn1-8.dll
|
||||
usr/bin/msys-aprutil-1-0.dll
|
||||
usr/bin/msys-apr-1-0.dll
|
||||
usr/bin/msguniq.exe
|
||||
usr/bin/msgunfmt.exe
|
||||
usr/bin/msgmerge.exe
|
||||
usr/bin/msginit.exe
|
||||
usr/bin/msggrep.exe
|
||||
usr/bin/msgfmt.exe
|
||||
usr/bin/msgfilter.exe
|
||||
usr/bin/msgexec.exe
|
||||
usr/bin/msgen.exe
|
||||
usr/bin/msgconv.exe
|
||||
usr/bin/msgcomm.exe
|
||||
usr/bin/msgcmp.exe
|
||||
usr/bin/msgcat.exe
|
||||
usr/bin/msgattrib.exe
|
||||
usr/bin/mpicalc.exe
|
||||
usr/bin/mount.exe
|
||||
usr/bin/mktemp.exe
|
||||
usr/bin/mkpasswd.exe
|
||||
usr/bin/mknod.exe
|
||||
usr/bin/mkgroup.exe
|
||||
usr/bin/mkfifo.exe
|
||||
usr/bin/mintty.exe
|
||||
usr/bin/mintheme
|
||||
usr/bin/minidumper.exe
|
||||
usr/bin/md5sum.exe
|
||||
usr/bin/mac2unix.exe
|
||||
usr/bin/lsattr.exe
|
||||
usr/bin/ls.exe
|
||||
usr/bin/logname.exe
|
||||
usr/bin/locate.exe
|
||||
usr/bin/locale.exe
|
||||
usr/bin/ln.exe
|
||||
usr/bin/link.exe
|
||||
usr/bin/lesskey.exe
|
||||
usr/bin/lessecho.exe
|
||||
usr/bin/less.exe
|
||||
usr/bin/ldh.exe
|
||||
usr/bin/ldd.exe
|
||||
usr/bin/kill.exe
|
||||
usr/bin/kbxutil.exe
|
||||
usr/bin/join.exe
|
||||
usr/bin/install.exe
|
||||
usr/bin/infotocap.exe
|
||||
usr/bin/infocmp.exe
|
||||
usr/bin/iconv.exe
|
||||
usr/bin/hostname.exe
|
||||
usr/bin/hostid.exe
|
||||
usr/bin/hmac256.exe
|
||||
usr/bin/head.exe
|
||||
usr/bin/gzip.exe
|
||||
usr/bin/gzexe
|
||||
usr/bin/gunzip
|
||||
usr/bin/gsettings.exe
|
||||
usr/bin/groups.exe
|
||||
usr/bin/gobject-query.exe
|
||||
usr/bin/gmondump.exe
|
||||
usr/bin/glib-compile-schemas.exe
|
||||
usr/bin/gkill.exe
|
||||
usr/bin/gio-querymodules.exe
|
||||
usr/bin/gettextize
|
||||
usr/bin/gettext.sh
|
||||
usr/bin/gettext.exe
|
||||
usr/bin/getflags
|
||||
usr/bin/getfacl.exe
|
||||
usr/bin/getemojis
|
||||
usr/bin/getconf.exe
|
||||
usr/bin/gencat.exe
|
||||
usr/bin/gdbus.exe
|
||||
usr/bin/gawk.exe
|
||||
usr/bin/gawk-5.0.0.exe
|
||||
usr/bin/gapplication.exe
|
||||
usr/bin/funzip.exe
|
||||
usr/bin/fold.exe
|
||||
usr/bin/fmt.exe
|
||||
usr/bin/find.exe
|
||||
usr/bin/file.exe
|
||||
usr/bin/fgrep
|
||||
usr/bin/false.exe
|
||||
usr/bin/factor.exe
|
||||
usr/bin/envsubst.exe
|
||||
usr/bin/env.exe
|
||||
usr/bin/egrep
|
||||
usr/bin/echo.exe
|
||||
usr/bin/dumpsexp.exe
|
||||
usr/bin/du.exe
|
||||
usr/bin/dos2unix.exe
|
||||
usr/bin/docx2txt.pl
|
||||
usr/bin/docx2txt
|
||||
usr/bin/dirmngr.exe
|
||||
usr/bin/dirmngr-client.exe
|
||||
usr/bin/dircolors.exe
|
||||
usr/bin/dir.exe
|
||||
usr/bin/diff3.exe
|
||||
usr/bin/diff.exe
|
||||
usr/bin/df.exe
|
||||
usr/bin/dd.exe
|
||||
usr/bin/date.exe
|
||||
usr/bin/d2u.exe
|
||||
usr/bin/cygwin-console-helper.exe
|
||||
usr/bin/cygcheck.exe
|
||||
usr/bin/cut.exe
|
||||
usr/bin/csplit.exe
|
||||
usr/bin/cp.exe
|
||||
usr/bin/comm.exe
|
||||
usr/bin/cmp.exe
|
||||
usr/bin/clear.exe
|
||||
usr/bin/cksum.exe
|
||||
usr/bin/chroot.exe
|
||||
usr/bin/chown.exe
|
||||
usr/bin/chmod.exe
|
||||
usr/bin/chgrp.exe
|
||||
usr/bin/chcon.exe
|
||||
usr/bin/chattr.exe
|
||||
usr/bin/cat.exe
|
||||
usr/bin/captoinfo.exe
|
||||
usr/bin/c_rehash
|
||||
usr/bin/bzless
|
||||
usr/bin/bzip2recover.exe
|
||||
usr/bin/bzip2.exe
|
||||
usr/bin/bzgrep
|
||||
usr/bin/bzfgrep
|
||||
usr/bin/bzegrep
|
||||
usr/bin/bzdiff
|
||||
usr/bin/bzcmp
|
||||
usr/bin/bzcat.exe
|
||||
usr/bin/bunzip2.exe
|
||||
usr/bin/bashbug
|
||||
usr/bin/basenc.exe
|
||||
usr/bin/basename.exe
|
||||
usr/bin/base64.exe
|
||||
usr/bin/base32.exe
|
||||
usr/bin/backup
|
||||
usr/bin/b2sum.exe
|
||||
usr/bin/autopoint
|
||||
usr/bin/astextplain
|
||||
usr/bin/arch.exe
|
||||
usr/bin/applygnupgdefaults
|
||||
usr/bin/addgnupghome
|
||||
usr/bin/[.exe
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script is used to build a customized Git Bash for windows pacakge that only include bash and no other unnecessary files
|
||||
|
||||
set -e
|
||||
|
||||
# Get script directory
|
||||
DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
cd "$DIR/../dist"
|
||||
|
||||
# Get the git distribution if it doesn't exist
|
||||
if [ ! -f /tmp/git.tar.bz2 ]; then
|
||||
# NOTE: Git for Windows v2.44 is the last release to support Windows 7 and 8
|
||||
URL="https://github.com/git-for-windows/git/releases/download/v2.44.0.windows.1/Git-2.44.0-32-bit.tar.bz2"
|
||||
echo "> Downloading git distribution"
|
||||
wget -q $URL -O /tmp/git.tar.bz2
|
||||
fi
|
||||
|
||||
# Unzip the git distribution to git directory
|
||||
# Ignore the unnecessary files
|
||||
# rm -rf git
|
||||
if [ ! -d /tmp/git ]; then
|
||||
mkdir -p /tmp/git
|
||||
echo "> Unzipping git distribution"
|
||||
tar -xf /tmp/git.tar.bz2 --exclude-from="$DIR/bash_ignore.txt" -C /tmp/git
|
||||
fi
|
||||
|
||||
# Copy the git distribution
|
||||
cp -r /tmp/git ./git
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script is used to build fastfetch inside a docker container.
|
||||
# The docker container tested is debian 10.
|
||||
|
||||
set -e
|
||||
|
||||
# Install required packages
|
||||
echo "Installing required packages..."
|
||||
apt-get update
|
||||
apt-get install --ignore-missing --no-install-recommends --no-install-suggests -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libdrm-dev git build-essential cmake imagemagick chafa libchafa-dev ddcutil
|
||||
|
||||
# Check if we're on debian 10
|
||||
apt-get install -y lsb-release
|
||||
if [ "$(lsb_release -cs)" != "buster" ]; then
|
||||
apt-get install --ignore-missing -y libegl-dev libglx-dev directx-headers-dev
|
||||
# else
|
||||
# echo "Backporting pacakges for debian 10"
|
||||
|
||||
# # Add Debian 11 "Bullseye" repository
|
||||
echo "deb http://deb.debian.org/debian/ bullseye main" > /etc/apt/sources.list.d/bullseye.list
|
||||
|
||||
# # Set package priorities to prefer Debian 10 (Buster) except for libegl-dev and its dependencies
|
||||
# echo 'Package: *
|
||||
# Pin: release n=buster
|
||||
# Pin-Priority: 900
|
||||
|
||||
# Package: libegl-dev
|
||||
# Pin: release n=bullseye
|
||||
# Pin-Priority: 990' > /etc/apt/preferences.d/priority
|
||||
|
||||
# apt-get update
|
||||
fi
|
||||
|
||||
# Clone repo
|
||||
echo "Cloning fastfetch..."
|
||||
git clone "https://github.com/fastfetch-cli/fastfetch"
|
||||
cd fastfetch
|
||||
|
||||
# Checkout the latest release tag
|
||||
latest_tag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
|
||||
echo "Checking out latest tag: $latest_tag"
|
||||
git checkout "$latest_tag"
|
||||
|
||||
# Backward compatibility
|
||||
# cmake 3.13: replace "NAME_WLE" with "NAME_WE"
|
||||
sed -i 's/NAME_WLE/NAME_WE/g' CMakeLists.txt
|
||||
|
||||
# Display system information
|
||||
echo "System Information:"
|
||||
uname -a
|
||||
|
||||
# Install linuxbrew packages
|
||||
# echo "Installing Linuxbrew..."
|
||||
# bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
# /home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa ddcutil --ignore-dependencies
|
||||
|
||||
# Configure project
|
||||
echo "Configuring project..."
|
||||
export PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
# Build project
|
||||
echo "Building project..."
|
||||
cmake --build . --target package -j 32
|
||||
|
||||
# List features of fastfetch
|
||||
echo "Listing features of fastfetch:"
|
||||
./fastfetch --list-features
|
||||
|
||||
# Run fastfetch
|
||||
echo "Running fastfetch:"
|
||||
time ./fastfetch -c presets/ci.jsonc
|
||||
|
||||
# Run fastfetch with JSON format
|
||||
echo "Running fastfetch with JSON format:"
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
# Run flashfetch
|
||||
echo "Running flashfetch:"
|
||||
time ./flashfetch
|
||||
|
||||
# Print dependencies of fastfetch
|
||||
echo "Dependencies of fastfetch:"
|
||||
ldd fastfetch
|
||||
|
||||
# Run tests
|
||||
echo "Running tests..."
|
||||
ctest
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Path to store extracted binaries
|
||||
output_dir="dist/binaries"
|
||||
|
||||
# Ensure output directory exists
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
build_platform() {
|
||||
container=$1
|
||||
platform=$2
|
||||
|
||||
echo "Running build in $container for $platform"
|
||||
|
||||
# Create a unique name for the container instance
|
||||
container_name="build_$platform"
|
||||
|
||||
# Run the build script inside the Docker container
|
||||
docker run -it --name "$container_name" -v "$PWD/tools:/tools" -w "/w" "$container" bash /tools/build_fastfetch.sh
|
||||
|
||||
# Extract binaries or packages produced by the build
|
||||
# Assuming binaries are in a specific directory, e.g., ./dist
|
||||
if docker cp "$container_name:/w/dist" "$output_dir/$platform"; then
|
||||
echo "Successfully extracted binaries for $platform"
|
||||
else
|
||||
echo "Failed to extract binaries for $platform"
|
||||
fi
|
||||
|
||||
# Cleanup: Remove the container after the build
|
||||
docker rm "$container_name"
|
||||
}
|
||||
|
||||
# Build for different platforms
|
||||
build_platform "amd64/debian:10-slim" "manylinux_2_28_x86_64"
|
||||
# build_platform "arm64v8/debian:10-slim" "manylinux_2_28_aaarch64"
|
||||
# build_platform "arm32v7/debian:10-slim" "manylinux_2_28_armv7l"
|
||||
# build_platform "riscv64/debian:sid-slim" "manylinux_2_37_riscv64"
|
||||
|
||||
echo "Build and extraction process completed."
|
||||
Executable
+138
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
FASTFETCH_VERSION="2.32.1"
|
||||
FASTFETCH_DL="https://github.com/fastfetch-cli/fastfetch/releases/download/$FASTFETCH_VERSION/"
|
||||
|
||||
# Get script directory
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
cd "$DIR/.."
|
||||
|
||||
set -e
|
||||
|
||||
# Remove the old build
|
||||
rm -rf dist/
|
||||
rm -rf build/
|
||||
|
||||
# Remove git from the source code before building
|
||||
rm -rf hyfetch/git/
|
||||
|
||||
# Build python from setup.py
|
||||
python3 setup.py sdist bdist_wheel
|
||||
|
||||
# Check
|
||||
twine check dist/*.tar.gz
|
||||
twine check dist/*.whl
|
||||
|
||||
# Build rust package
|
||||
"$DIR/build_rust.sh"
|
||||
|
||||
# =================
|
||||
# Build for windows
|
||||
cd dist
|
||||
|
||||
# Get the file name
|
||||
# file="$(ls | grep .whl)" use glob instead
|
||||
file=$(echo *-none-any.whl)
|
||||
|
||||
# Build bash pacakge
|
||||
"$DIR/build_bash.sh"
|
||||
|
||||
# Unzip the wheel
|
||||
echo "> Unzipping $file"
|
||||
rm -rf wheel
|
||||
unzip -qq "$file" -d wheel
|
||||
|
||||
# Copy the git distribution to the wheel
|
||||
cp -r git/ wheel/hyfetch/
|
||||
|
||||
# Embed fastfetch binary
|
||||
echo "> Embedding fastfetch binary"
|
||||
wget -q "$FASTFETCH_DL/fastfetch-windows-amd64.zip" -O fastfetch-windows.zip
|
||||
mkdir -p wheel/hyfetch/fastfetch
|
||||
bsdtar -zxf fastfetch-windows.zip -C wheel/hyfetch/fastfetch
|
||||
rm -rf fastfetch-windows.zip
|
||||
|
||||
# Embed rust binary
|
||||
echo "> Embedding rust binary"
|
||||
mkdir -p wheel/hyfetch/rust
|
||||
cp "$DIR/../target/x86_64-pc-windows-gnu/release/hyfetch.exe" wheel/hyfetch/rust/
|
||||
|
||||
# Edit .dist-info/WHEEL "Tag: {platform}" and rehash
|
||||
sed -i 's/Tag: py3-none-.*/Tag: py3-none-win32/' wheel/*.dist-info/WHEEL
|
||||
python "$DIR/build_rehash.py" wheel
|
||||
|
||||
# Zip to -win32.whl
|
||||
#new_name=${file/-any/-win32}
|
||||
#cd wheel && zip -qq -y -r "../$new_name" * && cd ..
|
||||
#twine check "$new_name"
|
||||
|
||||
# Zip to -win_amd64.whl
|
||||
# Since pypi doesn't allow two identical files with different names to be uploaded
|
||||
# We need to change the zip content a little bit for win_amd64
|
||||
new_name=${file/-any/-win_amd64}
|
||||
sed -i 's/Tag: py3-none-.*/Tag: py3-none-win_amd64/' wheel/*.dist-info/WHEEL
|
||||
python "$DIR/build_rehash.py" wheel
|
||||
cd wheel && zip -qq -y -r "../$new_name" * && cd ..
|
||||
twine check "$new_name"
|
||||
|
||||
# =================
|
||||
# Build for linux
|
||||
|
||||
# Now we're done with windows, delete the git folder
|
||||
rm -rf wheel/git
|
||||
|
||||
function build_for_platform() {
|
||||
ff_platform=$1
|
||||
wheel_platform=$2
|
||||
rust_platform=$3
|
||||
|
||||
echo "Building for $ff_platform"
|
||||
|
||||
# Download the fastfetch binary
|
||||
wget -q "$FASTFETCH_DL/fastfetch-$ff_platform.zip" -O "fastfetch-$ff_platform.zip"
|
||||
|
||||
# Delete the old fastfetch folder
|
||||
rm -rf wheel/hyfetch/fastfetch
|
||||
|
||||
# Unzip the fastfetch binary
|
||||
# unzip -qq "fastfetch-$ff_platform.zip" -d wheel/hyfetch/fastfetch
|
||||
mkdir -p wheel/hyfetch/fastfetch
|
||||
bsdtar -zxf "fastfetch-$ff_platform.zip" -C wheel/hyfetch/fastfetch --strip-components 1
|
||||
rm -rf "fastfetch-$ff_platform.zip"
|
||||
|
||||
# Copy rust binary to wheel/hyfetch/rust
|
||||
mkdir -p wheel/hyfetch/rust
|
||||
cp "$DIR/../target/$rust_platform/release/hyfetch" wheel/hyfetch/rust
|
||||
|
||||
# Change the file name
|
||||
new_name=${file/-any/-"$wheel_platform"}
|
||||
|
||||
# Edit WHEEL and rehash
|
||||
sed -i "s/Tag: py3-none-.*/Tag: py3-none-$wheel_platform/" wheel/*.dist-info/WHEEL
|
||||
python "$DIR/build_rehash.py" wheel
|
||||
|
||||
# Zip the wheel to platform.whl
|
||||
cd wheel && zip -qq -y -r "../$new_name" * && cd ..
|
||||
|
||||
# Check again
|
||||
twine check "$new_name"
|
||||
}
|
||||
|
||||
# See https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/
|
||||
# The official fastfetch build uses Ubuntu 20.04 with glibc 2.31
|
||||
build_for_platform "linux-amd64" "manylinux_2_31_x86_64" "x86_64-unknown-linux-musl"
|
||||
build_for_platform "linux-aarch64" "manylinux_2_31_aarch64" "aarch64-unknown-linux-musl"
|
||||
build_for_platform "linux-armv7l" "manylinux_2_31_armv7l" "armv7-unknown-linux-musleabihf"
|
||||
# There doesn't seem to be tags for freebsd?
|
||||
# build_for_platform "freebsd-amd64" "freebsd_x86_64"
|
||||
# build_for_platform "freebsd-aarch64" "freebsd_aarch64"
|
||||
build_for_platform "musl-amd64" "musllinux_1_1_x86_64" "x86_64-unknown-linux-musl"
|
||||
# build_for_platform "musl-aarch64" "musllinux_1_1_aarch64"
|
||||
# The official fastfetch build uses macOS 12.0
|
||||
build_for_platform "macos-universal" "macosx_11_0_x86_64" "x86_64-apple-darwin"
|
||||
build_for_platform "macos-universal" "macosx_11_0_arm64" "aarch64-apple-darwin"
|
||||
# TODO: linux_riscv64 (pypi's platform tag support is not there yet)
|
||||
# build_for_platform "linux-riscv64" "manylinux_2_31_riscv64"
|
||||
|
||||
# Finally, remove temporary files
|
||||
rm -rf wheel git
|
||||
@@ -0,0 +1,78 @@
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
from base64 import urlsafe_b64encode
|
||||
from pathlib import Path
|
||||
from typing import BinaryIO, Generator, Tuple, Any
|
||||
|
||||
"""
|
||||
The read_chunks, hash_file, and rehash functions below comes from pip._internal
|
||||
They are copied to ensure compatibility with future python versions.
|
||||
https://github.com/pypa/pip/blob/612515d2e0a6ff8676c139c096a45bc28b3456f4/src/pip/_internal/operations/install/wheel.py#L80
|
||||
"""
|
||||
|
||||
|
||||
def read_chunks(file: BinaryIO, size: int = io.DEFAULT_BUFFER_SIZE) -> Generator[bytes, None, None]:
|
||||
"""Yield pieces of data from a file-like object until EOF."""
|
||||
while True:
|
||||
chunk = file.read(size)
|
||||
if not chunk:
|
||||
break
|
||||
yield chunk
|
||||
|
||||
|
||||
def hash_file(path: Path, blocksize: int = 1 << 20) -> Tuple[Any, int]:
|
||||
"""Return (hash, length) for path using hashlib.sha256()"""
|
||||
|
||||
h = hashlib.sha256()
|
||||
length = 0
|
||||
with open(path, "rb") as f:
|
||||
for block in read_chunks(f, size=blocksize):
|
||||
length += len(block)
|
||||
h.update(block)
|
||||
return h, length
|
||||
|
||||
|
||||
def rehash(path: Path, blocksize: int = 1 << 20) -> Tuple[str, str]:
|
||||
"""Return (encoded_digest, length) for path using hashlib.sha256()"""
|
||||
h, length = hash_file(path, blocksize)
|
||||
digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=")
|
||||
return digest, str(length)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
agupa = argparse.ArgumentParser()
|
||||
agupa.add_argument("base_path", help="The path of the wheel")
|
||||
args = agupa.parse_args()
|
||||
|
||||
base_path = Path(args.base_path)
|
||||
|
||||
# Find the name of the dist-info path
|
||||
dist_info = next(base_path.rglob("*.dist-info"))
|
||||
|
||||
# Delete dist_info / record
|
||||
record = dist_info / "RECORD"
|
||||
order = record.read_text().splitlines()
|
||||
record.unlink()
|
||||
|
||||
# Rehash each file in the wheel
|
||||
processed = set()
|
||||
new_record = []
|
||||
for file in order:
|
||||
file = base_path / file.split(',')[0]
|
||||
if file.exists():
|
||||
digest, length = rehash(file)
|
||||
new_record.append(f"{str(file.relative_to(base_path)).replace("\\", "/")},{digest},{length}")
|
||||
processed.add(file)
|
||||
|
||||
for file in base_path.rglob('*'):
|
||||
if file.is_file() and file not in processed:
|
||||
digest, length = rehash(file)
|
||||
new_record.append(f"{str(file.relative_to(base_path)).replace("\\", "/")},{digest},{length}")
|
||||
|
||||
new_record.append(f"{str(dist_info.relative_to(base_path)).replace("\\", "/")}/RECORD,,")
|
||||
|
||||
# Write the new record
|
||||
record.write_text('\n'.join(new_record))
|
||||
print("Rehashed successfully")
|
||||
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "Building the Docker image..."
|
||||
docker build -t rust-builder-general tools/rust-builder/general
|
||||
|
||||
echo "Running the Docker container..."
|
||||
docker run --rm -v "$(pwd)":/app rust-builder-general bash -c "cd /app && ./tools/rust-builder/general/build.sh"
|
||||
|
||||
echo "Building MacOS Docker image..."
|
||||
docker build -t rust-builder-osx tools/rust-builder/osx
|
||||
|
||||
echo "Running MacOS Docker container..."
|
||||
docker run --rm -v "$(pwd)":/app rust-builder-osx bash -c "cd /app && ./tools/rust-builder/osx/build.sh"
|
||||
+4
-7
@@ -3,15 +3,12 @@
|
||||
# Stop on error
|
||||
set -e
|
||||
|
||||
# Remove old build
|
||||
rm -rf dist/*
|
||||
rm -rf build/*
|
||||
# Get script directory
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
# Build
|
||||
python setup.py sdist bdist_wheel
|
||||
# Run build_pkg
|
||||
"$DIR/build_pkg.sh"
|
||||
|
||||
# Check built files
|
||||
twine check dist/*
|
||||
|
||||
# Upload
|
||||
twine upload dist/*
|
||||
|
||||
+13
-5
@@ -10,6 +10,7 @@ import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
import regex
|
||||
import unicodedata
|
||||
from hypy_utils import write
|
||||
|
||||
from hyfetch.distros import AsciiArt
|
||||
@@ -34,7 +35,7 @@ def parse_ascii_distros() -> list[AsciiArt]:
|
||||
"""
|
||||
Parse ascii distros from neofetch script
|
||||
"""
|
||||
nf = (Path(__file__).parent.parent / 'neofetch').read_text().replace('\t', ' ' * 4)
|
||||
nf = (Path(__file__).parent.parent / 'neofetch').read_text('utf-8').replace('\t', ' ' * 4)
|
||||
|
||||
# Get the content of "get_distro_ascii" function
|
||||
nf = nf[nf.index('get_distro_ascii() {\n'):]
|
||||
@@ -139,18 +140,25 @@ def export_distro(d: AsciiArt) -> str:
|
||||
for s in string.punctuation + ' ':
|
||||
varname = varname.replace(s, '_')
|
||||
|
||||
# Remove accents
|
||||
varname = unicodedata.normalize('NFKD', varname).encode('ascii', 'ignore').decode('utf-8')
|
||||
|
||||
# Escape/unescape ascii
|
||||
ascii = d.ascii.replace("\\\\", "\\")
|
||||
while '"""' in ascii:
|
||||
ascii = ascii.replace('"""', '"\\""')
|
||||
|
||||
quotes = '"""'
|
||||
if '"""' in ascii:
|
||||
quotes = "'''"
|
||||
if "'''" in ascii:
|
||||
print(f"TODO: Cannot escape ascii because both \"\"\" and ''' exist: {ascii}")
|
||||
|
||||
script = f"""# This file is automatically generated. Please do not modify.
|
||||
|
||||
from . import AsciiArt
|
||||
|
||||
{varname} = AsciiArt(match=r'''{d.match}''', color='{d.color}', ascii=r\"""
|
||||
{varname} = AsciiArt(match=r'''{d.match}''', color='{d.color}', ascii=r{quotes}
|
||||
{ascii}
|
||||
\""")
|
||||
{quotes})
|
||||
"""
|
||||
write(Path(__file__).parent.parent / f'hyfetch/distros/{varname}.py', script)
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
if __name__ == '__main__':
|
||||
md = requests.get("https://raw.githubusercontent.com/lukas-w/font-logos/refs/heads/master/README.md").text
|
||||
md = [[field.strip().strip('`').replace('Linux', '').replace('GNU/', '').strip() for field in line.split('|')[1:-1]]
|
||||
for line in md.splitlines() if line.startswith("|") and 'fl-' in line]
|
||||
md = {name: chr(int(char, 16)) for name, css, code, char, img in md}
|
||||
Path("font_logos.json").write_text(json.dumps(md, indent=2, ensure_ascii=False))
|
||||
@@ -0,0 +1,27 @@
|
||||
FROM debian
|
||||
|
||||
# Install Rust building environment
|
||||
RUN apt update && apt install -y curl build-essential gcc libssl-dev bash musl-tools mingw-w64 \
|
||||
gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu gcc-riscv64-linux-gnu
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
# Add toolchains for building for musl[x86_64, aarch64, armv7l, riscv64], windows[x86_64]
|
||||
RUN /root/.cargo/bin/rustup target add \
|
||||
x86_64-unknown-linux-musl aarch64-unknown-linux-musl armv7-unknown-linux-musleabihf riscv64gc-unknown-linux-musl \
|
||||
x86_64-pc-windows-gnu
|
||||
|
||||
# Compile musl for multiple architectures
|
||||
RUN curl "https://musl.libc.org/releases/musl-1.2.5.tar.gz" --output musl.tgz && \
|
||||
tar -xvf musl.tgz && \
|
||||
cd musl-*/ && \
|
||||
./configure --prefix=/musl/aarch64 --exec-prefix=/musl/aarch64 --target=aarch64-linux-gnu --enable-wrapper=all && \
|
||||
make -j18 install && \
|
||||
make clean && \
|
||||
./configure --prefix=/musl/armv7l --exec-prefix=/musl/armv7l --target=arm-linux-gnueabihf --enable-wrapper=all && \
|
||||
make -j18 install && \
|
||||
make clean && \
|
||||
./configure --prefix=/musl/riscv64 --exec-prefix=/musl/riscv64 --target=riscv64-linux-gnu --enable-wrapper=all && \
|
||||
make -j18 install && \
|
||||
make clean && \
|
||||
cd .. && \
|
||||
rm -rf musl-*
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Add cargo to path
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
cd /app
|
||||
|
||||
# Set linkers for cross-compilation
|
||||
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc
|
||||
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=/musl/aarch64/bin/musl-gcc
|
||||
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER=/musl/armv7l/bin/musl-gcc
|
||||
export CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_LINKER=/musl/riscv64/bin/musl-gcc
|
||||
|
||||
# Build for windows
|
||||
cargo build --release --target x86_64-pc-windows-gnu
|
||||
|
||||
# Add -C relocation-model=static to the build command
|
||||
# See https://github.com/rust-lang/rust/issues/95926
|
||||
export RUSTFLAGS="-C relocation-model=static"
|
||||
|
||||
# Build for platforms
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
cargo build --release --target aarch64-unknown-linux-musl
|
||||
cargo build --release --target armv7-unknown-linux-musleabihf
|
||||
#cargo build --release --target riscv64gc-unknown-linux-musl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user