Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92623417f9 | |||
| 47e8a4b6ae | |||
| ce787650ff | |||
| f38da1da7b | |||
| aa2a46c307 | |||
| 249d88968a | |||
| 47ad86c1b3 | |||
| 3358918a96 | |||
| d210e8b717 | |||
| 2a0e05e228 | |||
| f80262b615 | |||
| 7aa46cb623 | |||
| 2a4242c57a | |||
| 8d3a3cc7f0 | |||
| eb76b2f474 | |||
| c9ed8e2088 | |||
| bee34b6262 | |||
| f4b91ddb9a | |||
| 348c6d4dcc | |||
| d9a46c97c8 | |||
| 06d299f2f7 | |||
| cd0f55cd92 | |||
| 5f31542362 | |||
| edd5d39f73 | |||
| 73bf1e7fdf | |||
| a511742239 | |||
| abed2f36e0 | |||
| b68e0ccf52 | |||
| 143bfd2db7 | |||
| 2e20d4e529 | |||
| 66ff8d06d9 | |||
| 887f766464 | |||
| 85eaad14f2 | |||
| 7682408554 | |||
| 9e85fbc151 | |||
| 10408af1a2 | |||
| 23814863f1 | |||
| 8d65521fad | |||
| d11796ef02 | |||
| 59f5fd5651 | |||
| a1d687d3bd | |||
| 7bd96e422f | |||
| d1f601048b | |||
| 3b2fb773d3 | |||
| fed771d930 | |||
| 8930f921b4 | |||
| 5de1bf65e4 | |||
| b46d0e96f4 | |||
| 1c67333b45 | |||
| 5e26a44c19 | |||
| 865dea5dc3 | |||
| d5eea9a8ef | |||
| 365aedafe6 | |||
| 399fcd3c64 | |||
| 299adb8e4d | |||
| f86792356e | |||
| 892ed4e141 | |||
| 5364e22f39 | |||
| 6d77ebc7a7 | |||
| 1eab4bb157 | |||
| d59e4a64d3 | |||
| 0dd67146d7 | |||
| 46b7771f2c | |||
| 002697b49a | |||
| 9a77c39170 | |||
| b744be3255 | |||
| e97c64e7c2 | |||
| 707eac5087 | |||
| b572e5d35f | |||
| 86af5598c6 | |||
| d7273909f6 | |||
| 7b4f209ebe | |||
| 46d00666e2 | |||
| 1cffe3556a | |||
| 07d94eec8b | |||
| 62469e6f52 | |||
| 2c8261dc9a | |||
| 3219547c92 | |||
| b0bc4e64a9 | |||
| 7bb049ea20 | |||
| 73becf78c4 | |||
| 7f949fe6b2 | |||
| 9abdae5fb4 | |||
| 6ff45d0edd | |||
| 99f99efc1c | |||
| 0c08b3950f | |||
| 2fc419476d | |||
| abac3b2bb3 | |||
| 43924305b9 | |||
| 29f771caec | |||
| 35f5e6eec9 | |||
| 0a7356a560 | |||
| 6a5edd037a | |||
| 2b5ef302a5 | |||
| 81364f55a2 |
@@ -6,12 +6,35 @@ neofetch with pride flags <3
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install Python >= 3.9 first. Then, just do:
|
### Method 1: Install using Python pip (Recommended)
|
||||||
|
|
||||||
|
Install Python >= 3.7 first. Then, just do:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install hyfetch
|
pip install hyfetch
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Method 2: Install using system package manager
|
||||||
|
|
||||||
|
Currently, these distributions have existing packages for HyFetch:
|
||||||
|
|
||||||
|
* ArchLinux: `yay -S hyfetch` (Thanks to @ Aleksana)
|
||||||
|
* Nix (Nixpkgs): `nix-env -i hyfetch` ([In Progress](https://github.com/NixOS/nixpkgs/pull/170309))
|
||||||
|
* Nix (NUR): ([In Progress](https://github.com/nix-community/NUR/pull/467))
|
||||||
|
* Guix: `guix install hyfetch` (Thanks to @ WammKD)
|
||||||
|
|
||||||
|
Currently, if you're using Nix the package manager or NixOS, you can use HyFetch with `nix-env -if https://github.com/hykilpikonna/hyfetch/tarball/master -A hyfetch`
|
||||||
|
|
||||||
|
> Now `hyfetch` is available in our NixOS-CN's flake. You can add [NixOS-CN](https://github.com/nixos-cn/flakes) in your [Nix Flake](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html) at first, then install package `hyfetch`.
|
||||||
|
> ```
|
||||||
|
> #flake.nix
|
||||||
|
>
|
||||||
|
> environment.systemPackages =
|
||||||
|
> [ nixos-cn.legacyPackages.${system}.hyfetch ];
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> (Thanks to @ YisuiDenghua and @ linyinfeng )
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
When you run `hyfetch` for the first time, it will prompt you to choose a color system and a preset. Just follow the prompt, and everything should work (hopefully). If something doesn't work, feel free to submit an issue!
|
When you run `hyfetch` for the first time, it will prompt you to choose a color system and a preset. Just follow the prompt, and everything should work (hopefully). If something doesn't work, feel free to submit an issue!
|
||||||
@@ -32,9 +55,67 @@ Feel free to experiment with it!
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
### TODO
|
||||||
|
|
||||||
|
* [ ] Add configuration to emphasize certain parts of the original ASCII art (to make icons like Fedora and Ubuntu look nicer)
|
||||||
|
* [ ] Paginate flags
|
||||||
|
|
||||||
|
### 1.1.2
|
||||||
|
|
||||||
|
* Add more flags ([#5](https://github.com/hykilpikonna/hyfetch/pull/5))
|
||||||
|
* Removed `numpy` dependency that was used in 1.1.0
|
||||||
|
|
||||||
|
### 1.1.0
|
||||||
|
|
||||||
|
* Refactored a lot of things
|
||||||
|
* Added Beiyang flag xD
|
||||||
|
* Added interactive configurator for brightness adjustment
|
||||||
|
* Added dark/light mode selection
|
||||||
|
* Added color bar preview for RGB/8bit mode selection
|
||||||
|
* Added random color arrangement feature (for NixOS)
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/22280294/180901539-014f036e-c926-4470-ac72-a6d6dcf30672.png" width="100px" />
|
||||||
|
|
||||||
|
### 1.0.7
|
||||||
|
|
||||||
|
* Fix: Make config path not on init but when it's actually needed.
|
||||||
|
|
||||||
|
### 1.0.6
|
||||||
|
|
||||||
|
* Remove `hypy_utils` dependency to make packaging easier.
|
||||||
|
|
||||||
|
### 1.0.5
|
||||||
|
|
||||||
|
* Fix terminal emulator detection ([PR #2](https://github.com/hykilpikonna/hyfetch/pull/2))
|
||||||
|
|
||||||
|
### 1.0.4
|
||||||
|
|
||||||
|
* Add more flags ([PR #1](https://github.com/hykilpikonna/hyfetch/pull/1))
|
||||||
|
|
||||||
|
### 1.0.3
|
||||||
|
|
||||||
|
* Fix missing dependency for setuptools
|
||||||
|
|
||||||
|
### 1.0.2
|
||||||
|
|
||||||
|
* Implement RGB to 8bit conversion
|
||||||
|
* Add support for Python 3.7 and 3.8
|
||||||
|
|
||||||
|
### 1.0.1
|
||||||
|
|
||||||
|
* Included 11 flag presets
|
||||||
|
* Ability to lighten colors with `--c-set-l <lightness>`
|
||||||
|
* Command-line flag chooser
|
||||||
|
* Supports Python >= 3.9
|
||||||
|
|
||||||
## More Screenshots
|
## More Screenshots
|
||||||
|
|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
|
## Original Readme from Neofetch Below
|
||||||
|
|
||||||
<h3 align="center"><img src="https://i.imgur.com/ZQI2EYz.png" alt="logo" height="100px"></h3>
|
<h3 align="center"><img src="https://i.imgur.com/ZQI2EYz.png" alt="logo" height="100px"></h3>
|
||||||
<p align="center">A command-line system information tool written in bash 3.2+</p>
|
<p align="center">A command-line system information tool written in bash 3.2+</p>
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
with import <nixpkgs> {};
|
||||||
|
|
||||||
|
rec {
|
||||||
|
hyfetch = python3Packages.buildPythonPackage rec {
|
||||||
|
pname = "HyFetch";
|
||||||
|
version = "1.0.7";
|
||||||
|
|
||||||
|
src = pythonPackages.fetchPypi {
|
||||||
|
inherit pname version;
|
||||||
|
hash = "sha256-3/6/3EtTqHXTMuRIo2nclIxYSzOFvQegR29OJsKMQU4=";
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = with python3Packages; [
|
||||||
|
typing-extensions
|
||||||
|
setuptools
|
||||||
|
];
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "neofetch with pride flags <3";
|
||||||
|
longDescription = ''
|
||||||
|
HyFetch is a command-line system information tool fork of neofetch.
|
||||||
|
HyFetch displays information about your system next to your OS logo
|
||||||
|
in ASCII representation. The ASCII representation is then colored in
|
||||||
|
the pattern of the pride flag of your choice. The main purpose of
|
||||||
|
HyFetch is to be used in screenshots to show other users what
|
||||||
|
operating system or distribution you are running, what theme or
|
||||||
|
icon set you are using, etc.
|
||||||
|
'';
|
||||||
|
homepage = "https://github.com/hykilpikonna/HyFetch";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "hyfetch";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from . import main
|
from . import main
|
||||||
|
|
||||||
|
__version__ = main.VERSION
|
||||||
__version__ = '1.0.2'
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
This version of color_scale is a special version made without numpy dependency. The numpy version
|
||||||
|
would be faster, but numpy is 11 MB large. In comparison, hyfetch 1.0.7 is only 105 kB, so it's not
|
||||||
|
a good idea to depend on numpy.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .color_util import RGB
|
||||||
|
|
||||||
|
|
||||||
|
def create_gradient_hex(colors: list[str], resolution: int = 300) -> list[RGB]:
|
||||||
|
"""
|
||||||
|
Create gradient array from hex
|
||||||
|
"""
|
||||||
|
colors = [RGB.from_hex(s) for s in colors]
|
||||||
|
return create_gradient(colors, resolution)
|
||||||
|
|
||||||
|
|
||||||
|
def create_gradient(colors: list[RGB], resolution: int) -> list[RGB]:
|
||||||
|
"""
|
||||||
|
Create gradient array
|
||||||
|
|
||||||
|
Usage: arr[ratio / len(arr)] = Scaled gradient color at that point
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
|
||||||
|
# Create gradient mapping
|
||||||
|
for i in range(len(colors) - 1):
|
||||||
|
c1 = colors[i]
|
||||||
|
c2 = colors[i + 1]
|
||||||
|
bi = i * resolution
|
||||||
|
|
||||||
|
for ri in range(resolution):
|
||||||
|
ratio = ri / resolution
|
||||||
|
r = int(c2.r * ratio + c1.r * (1 - ratio))
|
||||||
|
g = int(c2.g * ratio + c1.g * (1 - ratio))
|
||||||
|
b = int(c2.b * ratio + c1.b * (1 - ratio))
|
||||||
|
result.append(RGB(r, g, b))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_raw(gradient: list[RGB], ratio: float) -> RGB:
|
||||||
|
"""
|
||||||
|
:param gradient: Gradient array (2d)
|
||||||
|
:param ratio: Between 0-1
|
||||||
|
:return: RGB subarray (1d, has 3 values)
|
||||||
|
"""
|
||||||
|
if ratio == 1:
|
||||||
|
return gradient[-1]
|
||||||
|
|
||||||
|
i = int(ratio * len(gradient))
|
||||||
|
return gradient[i]
|
||||||
|
|
||||||
|
|
||||||
|
class Scale:
|
||||||
|
colors: list[RGB]
|
||||||
|
rgb: list[RGB]
|
||||||
|
|
||||||
|
def __init__(self, scale: list[str], resolution: int = 300):
|
||||||
|
self.colors = [RGB.from_hex(s) for s in scale]
|
||||||
|
self.rgb = create_gradient(self.colors, resolution)
|
||||||
|
|
||||||
|
def __call__(self, ratio: float) -> RGB:
|
||||||
|
"""
|
||||||
|
:param ratio: Between 0-1
|
||||||
|
"""
|
||||||
|
return get_raw(self.rgb, ratio)
|
||||||
|
|
||||||
|
|
||||||
|
def test_color_scale():
|
||||||
|
scale = Scale(['#232323', '#4F1879', '#B43A78', '#F98766', '#FCFAC0'])
|
||||||
|
|
||||||
|
colors = 100
|
||||||
|
for i in range(colors + 1):
|
||||||
|
print(scale(i / colors).to_ansi_rgb(False), end=' ')
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from numpy import ndarray
|
||||||
|
|
||||||
|
from .color_util import RGB
|
||||||
|
|
||||||
|
|
||||||
|
def create_gradient_hex(colors: list[str], resolution: int = 300) -> ndarray:
|
||||||
|
"""
|
||||||
|
Create gradient array from hex
|
||||||
|
"""
|
||||||
|
colors = np.array([RGB.from_hex(s) for s in colors])
|
||||||
|
return create_gradient(colors, resolution)
|
||||||
|
|
||||||
|
|
||||||
|
def create_gradient(colors: ndarray, resolution: int) -> ndarray:
|
||||||
|
"""
|
||||||
|
Create gradient 2d array.
|
||||||
|
|
||||||
|
Usage: arr[ratio / len(arr), :] = Scaled gradient color at that point
|
||||||
|
"""
|
||||||
|
result = np.zeros((resolution * (len(colors) - 1), 3), dtype='uint8')
|
||||||
|
|
||||||
|
# Create gradient mapping
|
||||||
|
for i in range(len(colors) - 1):
|
||||||
|
c1 = colors[i, :]
|
||||||
|
c2 = colors[i + 1, :]
|
||||||
|
bi = i * resolution
|
||||||
|
|
||||||
|
for r in range(resolution):
|
||||||
|
ratio = r / resolution
|
||||||
|
result[bi + r, :] = c2 * ratio + c1 * (1 - ratio)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_raw(gradient: ndarray, ratio: float) -> ndarray:
|
||||||
|
"""
|
||||||
|
:param gradient: Gradient array (2d)
|
||||||
|
:param ratio: Between 0-1
|
||||||
|
:return: RGB subarray (1d, has 3 values)
|
||||||
|
"""
|
||||||
|
if ratio == 1:
|
||||||
|
return gradient[-1, :]
|
||||||
|
|
||||||
|
i = int(ratio * len(gradient))
|
||||||
|
return gradient[i, :]
|
||||||
|
|
||||||
|
|
||||||
|
class Scale:
|
||||||
|
colors: ndarray
|
||||||
|
rgb: ndarray
|
||||||
|
|
||||||
|
def __init__(self, scale: list[str], resolution: int = 300):
|
||||||
|
self.colors = np.array([RGB.from_hex(s) for s in scale])
|
||||||
|
self.rgb = create_gradient(self.colors, resolution)
|
||||||
|
|
||||||
|
def __call__(self, ratio: float) -> RGB:
|
||||||
|
"""
|
||||||
|
:param ratio: Between 0-1
|
||||||
|
"""
|
||||||
|
return RGB(*get_raw(self.rgb, ratio))
|
||||||
|
|
||||||
|
|
||||||
|
def test_color_scale():
|
||||||
|
scale = Scale(['#232323', '#4F1879', '#B43A78', '#F98766', '#FCFAC0'])
|
||||||
|
|
||||||
|
colors = 100
|
||||||
|
for i in range(colors + 1):
|
||||||
|
print(scale(i / colors).to_ansi_rgb(False), end=' ')
|
||||||
+80
-5
@@ -1,10 +1,69 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import colorsys
|
import colorsys
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple, Callable, Optional
|
||||||
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
from .constants import GLOBAL_CFG
|
||||||
|
|
||||||
AnsiMode = Literal['default', 'ansi', '8bit', 'rgb']
|
AnsiMode = Literal['default', 'ansi', '8bit', 'rgb']
|
||||||
|
LightDark = Literal['light', 'dark']
|
||||||
|
|
||||||
|
|
||||||
|
MINECRAFT_COLORS = ["&0/\033[0;30m", "&1/\033[0;34m", "&2/\033[0;32m", "&3/\033[0;36m", "&4/\033[0;31m",
|
||||||
|
"&5/\033[0;35m", "&6/\033[0;33m", "&7/\033[0;37m", "&8/\033[1;30m", "&9/\033[1;34m",
|
||||||
|
"&a/\033[1;32m", "&b/\033[1;36m", "&c/\033[1;31m", "&d/\033[1;35m", "&e/\033[1;33m",
|
||||||
|
"&f/\033[1;37m",
|
||||||
|
"&r/\033[0m", "&l/\033[1m", "&o/\033[3m", "&n/\033[4m", "&-/\n"]
|
||||||
|
MINECRAFT_COLORS = [(r[:2], r[3:]) for r in MINECRAFT_COLORS]
|
||||||
|
|
||||||
|
|
||||||
|
def color(msg: str) -> str:
|
||||||
|
"""
|
||||||
|
Replace extended minecraft color codes in string
|
||||||
|
:param msg: Message with minecraft color codes
|
||||||
|
:return: Message with escape codes
|
||||||
|
"""
|
||||||
|
for code, esc in MINECRAFT_COLORS:
|
||||||
|
msg = msg.replace(code, esc)
|
||||||
|
|
||||||
|
while '&gf(' in msg or '&gb(' in msg:
|
||||||
|
i = msg.index('&gf(') if '&gf(' in msg else msg.index('&gb(')
|
||||||
|
end = msg.index(')', i)
|
||||||
|
code = msg[i + 4:end]
|
||||||
|
fore = msg[i + 2] == 'f'
|
||||||
|
|
||||||
|
if code.startswith('#'):
|
||||||
|
rgb = tuple(int(code.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
|
||||||
|
else:
|
||||||
|
code = code.replace(',', ' ').replace(';', ' ').replace(' ', ' ')
|
||||||
|
rgb = tuple(int(c) for c in code.split(' '))
|
||||||
|
|
||||||
|
msg = msg[:i] + RGB(*rgb).to_ansi(foreground=fore) + msg[end + 1:]
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def printc(msg: str):
|
||||||
|
"""
|
||||||
|
Print with color
|
||||||
|
:param msg: Message with minecraft color codes
|
||||||
|
"""
|
||||||
|
print(color(msg + '&r'))
|
||||||
|
|
||||||
|
|
||||||
|
def clear_screen(title: str = ''):
|
||||||
|
"""
|
||||||
|
Clear screen using ANSI escape codes
|
||||||
|
"""
|
||||||
|
if not GLOBAL_CFG.debug:
|
||||||
|
print('\033[2J\033[H', end='')
|
||||||
|
|
||||||
|
if title:
|
||||||
|
print()
|
||||||
|
printc(title)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
def redistribute_rgb(r: int, g: int, b: int) -> tuple[int, int, int]:
|
def redistribute_rgb(r: int, g: int, b: int) -> tuple[int, int, int]:
|
||||||
@@ -99,7 +158,9 @@ class RGB(NamedTuple):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def to_ansi(self, mode: AnsiMode, foreground: bool = True):
|
def to_ansi(self, mode: AnsiMode | None = None, foreground: bool = True):
|
||||||
|
if not mode:
|
||||||
|
mode = GLOBAL_CFG.color_mode
|
||||||
if mode == 'rgb':
|
if mode == 'rgb':
|
||||||
return self.to_ansi_rgb(foreground)
|
return self.to_ansi_rgb(foreground)
|
||||||
if mode == '8bit':
|
if mode == '8bit':
|
||||||
@@ -116,12 +177,26 @@ class RGB(NamedTuple):
|
|||||||
"""
|
"""
|
||||||
return RGB(*redistribute_rgb(*[v * multiplier for v in self]))
|
return RGB(*redistribute_rgb(*[v * multiplier for v in self]))
|
||||||
|
|
||||||
def set_light(self, light: int) -> 'RGB':
|
def set_light(self, light: float, at_least: bool | None = None, at_most: bool | None = None) -> 'RGB':
|
||||||
"""
|
"""
|
||||||
Set HSL lightness value
|
Set HSL lightness value
|
||||||
|
|
||||||
:param light: Lightness value
|
:param light: Lightness value (0-1)
|
||||||
|
:param at_least: Set the lightness to at least this value (no change if greater)
|
||||||
|
:param at_most: Set the lightness to at most this value (no change if lesser)
|
||||||
:return: New color (original isn't modified)
|
:return: New color (original isn't modified)
|
||||||
"""
|
"""
|
||||||
|
# Convert to HSL
|
||||||
h, l, s = colorsys.rgb_to_hls(*[v / 255.0 for v in self])
|
h, l, s = colorsys.rgb_to_hls(*[v / 255.0 for v in self])
|
||||||
return RGB(*[round(v * 255.0) for v in colorsys.hls_to_rgb(h, light, s)])
|
|
||||||
|
# Modify light value
|
||||||
|
if at_least is None and at_most is None:
|
||||||
|
l = light
|
||||||
|
else:
|
||||||
|
if at_most:
|
||||||
|
l = min(l, light)
|
||||||
|
if at_least:
|
||||||
|
l = max(l, light)
|
||||||
|
|
||||||
|
# Convert back to RGB
|
||||||
|
return RGB(*[round(v * 255.0) for v in colorsys.hls_to_rgb(h, l, s)])
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
CONFIG_PATH = Path.home() / '.config/hyfetch.json'
|
||||||
|
VERSION = '1.1.2'
|
||||||
|
|
||||||
|
# Obtain terminal size
|
||||||
|
try:
|
||||||
|
TERM_LEN = os.get_terminal_size().columns
|
||||||
|
except Exception:
|
||||||
|
TERM_LEN = 40
|
||||||
|
|
||||||
|
TEST_ASCII = r"""
|
||||||
|
### |\___/| ###
|
||||||
|
### ) ( ###
|
||||||
|
## =\ /= ##
|
||||||
|
#### )===( ####
|
||||||
|
### / \ ###
|
||||||
|
### | | ###
|
||||||
|
## / {txt} \ ##
|
||||||
|
## \ / ##
|
||||||
|
_/\_\_ _/_/\_
|
||||||
|
|##| ( ( |##|
|
||||||
|
|##| ) ) |##|
|
||||||
|
|##| (_( |##|""".strip('\n')
|
||||||
|
|
||||||
|
TEST_ASCII_WIDTH = max(len(line) for line in TEST_ASCII.split('\n'))
|
||||||
|
DEFAULT_DARK_L = 0.
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GlobalConfig:
|
||||||
|
# Global color mode default to 8-bit for compatibility
|
||||||
|
color_mode: str
|
||||||
|
override_distro: str | None
|
||||||
|
debug: bool
|
||||||
|
is_light: bool
|
||||||
|
|
||||||
|
def light_dark(self) -> Literal['light', 'dark']:
|
||||||
|
return 'light' if self.is_light else 'dark'
|
||||||
|
|
||||||
|
def default_lightness(self, term: Literal['light', 'dark'] | None = None) -> float:
|
||||||
|
if term is None:
|
||||||
|
term = self.light_dark()
|
||||||
|
return 0.65 if term.lower() == 'dark' else 0.4
|
||||||
|
|
||||||
|
|
||||||
|
GLOBAL_CFG = GlobalConfig(color_mode='8bit', override_distro=None, debug=False, is_light=False)
|
||||||
+219
-66
@@ -3,27 +3,18 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass
|
import random
|
||||||
from pathlib import Path
|
import re
|
||||||
|
from itertools import permutations
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from hypy_utils import printc, json_stringify, color
|
from hyfetch import presets
|
||||||
|
|
||||||
from .color_util import AnsiMode
|
from .color_util import printc, color, clear_screen, LightDark
|
||||||
from .neofetch_util import run_neofetch
|
from .constants import CONFIG_PATH, VERSION, TERM_LEN, TEST_ASCII_WIDTH, TEST_ASCII, GLOBAL_CFG
|
||||||
from .presets import PRESETS, ColorProfile
|
from .models import Config
|
||||||
|
from .neofetch_util import run_neofetch, get_distro_ascii, ColorAlignment, ascii_size, color_alignments
|
||||||
CONFIG_PATH = Path.home() / '.config/hyfetch.json'
|
from .presets import PRESETS
|
||||||
CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Config:
|
|
||||||
preset: str
|
|
||||||
mode: AnsiMode
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
CONFIG_PATH.write_text(json_stringify(self), 'utf-8')
|
|
||||||
|
|
||||||
|
|
||||||
def check_config() -> Config:
|
def check_config() -> Config:
|
||||||
@@ -36,25 +27,33 @@ def check_config() -> Config:
|
|||||||
:return: Config object
|
:return: Config object
|
||||||
"""
|
"""
|
||||||
if CONFIG_PATH.is_file():
|
if CONFIG_PATH.is_file():
|
||||||
return Config(**json.loads(CONFIG_PATH.read_text('utf-8')))
|
try:
|
||||||
|
return Config.from_dict(json.loads(CONFIG_PATH.read_text('utf-8')))
|
||||||
|
except KeyError:
|
||||||
|
return create_config()
|
||||||
|
|
||||||
return create_config()
|
return create_config()
|
||||||
|
|
||||||
|
|
||||||
def literal_input(prompt: str, options: Iterable[str], default: str) -> str:
|
def literal_input(prompt: str, options: Iterable[str], default: str, show_ops: bool = True) -> str:
|
||||||
"""
|
"""
|
||||||
Ask the user to provide an input among a list of options
|
Ask the user to provide an input among a list of options
|
||||||
|
|
||||||
:param prompt: Input prompt
|
:param prompt: Input prompt
|
||||||
:param options: Options
|
:param options: Options
|
||||||
:param default: Default option
|
:param default: Default option
|
||||||
|
:param show_ops: Show options
|
||||||
:return: Selection
|
:return: Selection
|
||||||
"""
|
"""
|
||||||
options = list(options)
|
options = list(options)
|
||||||
lows = [o.lower() for o in options]
|
lows = [o.lower() for o in options]
|
||||||
|
|
||||||
op_text = '|'.join([f'&l&n{o}&r' if o == default else o for o in options])
|
if show_ops:
|
||||||
printc(f'{prompt} ({op_text})')
|
op_text = '|'.join([f'&l&n{o}&r' if o == default else o for o in options])
|
||||||
|
printc(f'{prompt} ({op_text})')
|
||||||
|
else:
|
||||||
|
printc(f'{prompt} (default: {default})')
|
||||||
|
|
||||||
selection = input('> ') or default
|
selection = input('> ') or default
|
||||||
while not selection.lower() in lows:
|
while not selection.lower() in lows:
|
||||||
print(f'Invalid selection! {selection} is not one of {"|".join(options)}')
|
print(f'Invalid selection! {selection} is not one of {"|".join(options)}')
|
||||||
@@ -64,65 +63,180 @@ def literal_input(prompt: str, options: Iterable[str], default: str) -> str:
|
|||||||
return options[lows.index(selection)]
|
return options[lows.index(selection)]
|
||||||
|
|
||||||
|
|
||||||
def center_text(txt: str, spaces: int) -> str:
|
|
||||||
"""
|
|
||||||
Put the text in the center in a defined space
|
|
||||||
|
|
||||||
>>> center_text('meow', 9)
|
|
||||||
' meow '
|
|
||||||
|
|
||||||
:param txt: Text
|
|
||||||
:param spaces: Total space of the text
|
|
||||||
:return: Text with length spaces
|
|
||||||
"""
|
|
||||||
spaces -= len(txt)
|
|
||||||
|
|
||||||
if spaces % 2 == 1:
|
|
||||||
spaces -= 1
|
|
||||||
txt += ' '
|
|
||||||
|
|
||||||
while spaces > 0:
|
|
||||||
spaces -= 2
|
|
||||||
txt = f' {txt} '
|
|
||||||
|
|
||||||
return txt
|
|
||||||
|
|
||||||
|
|
||||||
def create_config() -> Config:
|
def create_config() -> Config:
|
||||||
"""
|
"""
|
||||||
Create config interactively
|
Create config interactively
|
||||||
|
|
||||||
:return: Config object (automatically stored)
|
:return: Config object (automatically stored)
|
||||||
"""
|
"""
|
||||||
# Select color system
|
title = 'Welcome to &b&lhy&f&lfetch&r! Let\'s set up some colors first.'
|
||||||
# TODO: Demo of each color system
|
clear_screen(title)
|
||||||
color_system = literal_input('Which &acolor &bsystem &rdo you want to use?',
|
|
||||||
['8bit', 'rgb'], 'rgb')
|
|
||||||
|
|
||||||
# Print preset
|
##############################
|
||||||
print('Available presets:\n')
|
# 1. Select color system
|
||||||
spacing = max(max(len(k) for k in PRESETS.keys()), 30)
|
try:
|
||||||
|
# Demonstrate RGB with a gradient. This requires numpy
|
||||||
|
from .color_scale import Scale
|
||||||
|
|
||||||
|
scale2 = Scale(['#12c2e9', '#c471ed', '#f7797d'])
|
||||||
|
_8bit = [scale2(i / TERM_LEN).to_ansi_8bit(False) for i in range(TERM_LEN)]
|
||||||
|
_rgb = [scale2(i / TERM_LEN).to_ansi_rgb(False) for i in range(TERM_LEN)]
|
||||||
|
|
||||||
|
printc('&f' + ''.join(c + t for c, t in zip(_8bit, '8bit Color Testing'.center(TERM_LEN))))
|
||||||
|
printc('&f' + ''.join(c + t for c, t in zip(_rgb, 'RGB Color Testing'.center(TERM_LEN))))
|
||||||
|
|
||||||
|
print()
|
||||||
|
printc(f'&a1. Which &bcolor system &ado you want to use?')
|
||||||
|
printc(f'(If you can\'t see colors under "RGB Color Testing", please choose 8bit)')
|
||||||
|
print()
|
||||||
|
color_system = literal_input('Your choice?', ['8bit', 'rgb'], 'rgb')
|
||||||
|
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
# Numpy not found, skip gradient test, use fallback
|
||||||
|
color_system = literal_input('Which &acolor &bsystem &rdo you want to use?',
|
||||||
|
['8bit', 'rgb'], 'rgb')
|
||||||
|
|
||||||
|
# Override global color mode
|
||||||
|
GLOBAL_CFG.color_mode = color_system
|
||||||
|
title += f'\n&e1. Selected color mode: &r{color_system}'
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# 2. Select light/dark mode
|
||||||
|
clear_screen(title)
|
||||||
|
light_dark = literal_input(f'2. Is your terminal in &gf(#85e7e9)light mode&r or &gf(#c471ed)dark mode&r?',
|
||||||
|
['light', 'dark'], 'dark')
|
||||||
|
is_light = light_dark == 'light'
|
||||||
|
GLOBAL_CFG.is_light = is_light
|
||||||
|
title += f'\n&e2. Light/Dark: &r{light_dark}'
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# 3. Choose preset
|
||||||
|
clear_screen(title)
|
||||||
|
printc('&a3. Let\'s choose a flag!')
|
||||||
|
printc('Available flag presets:')
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Create flags = [[lines]]
|
||||||
flags = []
|
flags = []
|
||||||
|
spacing = max(max(len(k) for k in PRESETS.keys()), 20)
|
||||||
for name, preset in PRESETS.items():
|
for name, preset in PRESETS.items():
|
||||||
flags.append([preset.color_text(' ' * spacing, foreground=False),
|
flag = preset.color_text(' ' * spacing, foreground=False)
|
||||||
'&0' + preset.color_text(center_text(name, spacing), foreground=False),
|
flags.append([name.center(spacing), flag, flag, flag])
|
||||||
preset.color_text(' ' * spacing, foreground=False)])
|
|
||||||
flags_per_row = 3
|
# Calculate flags per row
|
||||||
|
flags_per_row = TERM_LEN // (spacing + 2)
|
||||||
while flags:
|
while flags:
|
||||||
current = flags[:flags_per_row]
|
current = flags[:flags_per_row]
|
||||||
flags = flags[flags_per_row:]
|
flags = flags[flags_per_row:]
|
||||||
for line in range(len(current[0])):
|
|
||||||
printc(' '.join(flag[line] for flag in current))
|
# Print by row
|
||||||
|
[printc(' '.join(line)) for line in zip(*current)]
|
||||||
print()
|
print()
|
||||||
|
|
||||||
print()
|
print()
|
||||||
tmp = PRESETS['rainbow'].color_text('preset')
|
tmp = PRESETS['rainbow'].set_light_dl_def(light_dark).color_text('preset')
|
||||||
preset = literal_input(f'Which {tmp} do you want to use?', PRESETS.keys(), 'rainbow')
|
preset = literal_input(f'Which {tmp} do you want to use?', PRESETS.keys(), 'rainbow', show_ops=False)
|
||||||
|
_prs = PRESETS[preset]
|
||||||
|
title += f'\n&e3. Selected flag: &r{_prs.color_text(preset)}'
|
||||||
|
|
||||||
|
#############################
|
||||||
|
# 4. Dim/lighten colors
|
||||||
|
clear_screen(title)
|
||||||
|
printc(f'&a4. Let\'s adjust the color brightness!')
|
||||||
|
printc(f'The colors might be a little bit too {"bright" if is_light else "dark"} for {light_dark} mode.')
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Print cats
|
||||||
|
num_cols = TERM_LEN // (TEST_ASCII_WIDTH + 2)
|
||||||
|
ratios = [col / (num_cols - 1) for col in range(num_cols)]
|
||||||
|
ratios = [(r * 0.4 + 0.1) if is_light else (r * 0.4 + 0.5) for r in ratios]
|
||||||
|
lines = [ColorAlignment('horizontal').recolor_ascii(TEST_ASCII.replace(
|
||||||
|
'{txt}', f'{r * 100:.0f}%'.center(5)), _prs.set_light_dl(r, light_dark)).split('\n') for r in ratios]
|
||||||
|
[printc(' '.join(line)) for line in zip(*lines)]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print()
|
||||||
|
printc(f'Which brightness level look the best? (Default: left blank = {GLOBAL_CFG.default_lightness(light_dark):.2f} for {light_dark} mode)')
|
||||||
|
lightness = input('> ').strip().lower() or None
|
||||||
|
|
||||||
|
# Parse lightness
|
||||||
|
if not lightness or lightness in ['unset', 'none']:
|
||||||
|
lightness = None
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
lightness = int(lightness[:-1]) / 100 if lightness.endswith('%') else float(lightness)
|
||||||
|
assert 0 <= lightness <= 1
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
printc('&cUnable to parse lightness value, please input it as a decimal or percentage (e.g. 0.5 or 50%)')
|
||||||
|
|
||||||
|
if lightness:
|
||||||
|
_prs = _prs.set_light_dl(lightness, light_dark)
|
||||||
|
title += f'\n&e4. Brightness: &r{f"{lightness:.2f}" if lightness else "unset"}'
|
||||||
|
|
||||||
|
#############################
|
||||||
|
# 5. Color arrangement
|
||||||
|
color_alignment = None
|
||||||
|
while True:
|
||||||
|
clear_screen(title)
|
||||||
|
printc(f'&a5. Let\'s choose a color arrangement!')
|
||||||
|
printc(f'You can choose standard horizontal or vertical alignment, or use one of the random color schemes, or assign colors yourself (TODO).')
|
||||||
|
print()
|
||||||
|
|
||||||
|
asc = get_distro_ascii()
|
||||||
|
asc_width = ascii_size(asc)[0]
|
||||||
|
asciis = [
|
||||||
|
[*ColorAlignment('horizontal').recolor_ascii(asc, _prs).split('\n'), 'Horizontal'.center(asc_width)],
|
||||||
|
[*ColorAlignment('vertical').recolor_ascii(asc, _prs).split('\n'), 'Vertical'.center(asc_width)],
|
||||||
|
]
|
||||||
|
ascii_per_row = TERM_LEN // (asc_width + 2)
|
||||||
|
|
||||||
|
# Random color schemes
|
||||||
|
# ascii_indices =
|
||||||
|
pis = list(range(len(_prs.unique_colors().colors)))
|
||||||
|
while len(pis) < len(set(re.findall('(?<=\\${c)[0-9](?=})', asc))):
|
||||||
|
pis += pis
|
||||||
|
perm = list(permutations(pis))
|
||||||
|
random_count = ascii_per_row * 2 - 2
|
||||||
|
choices = random.sample(perm, random_count)
|
||||||
|
choices = [{i: n for i, n in enumerate(c)} for c in choices]
|
||||||
|
asciis += [[*ColorAlignment('custom', r).recolor_ascii(asc, _prs).split('\n'), f'random{i}'.center(asc_width)]
|
||||||
|
for i, r in enumerate(choices)]
|
||||||
|
|
||||||
|
while asciis:
|
||||||
|
current = asciis[:ascii_per_row]
|
||||||
|
asciis = asciis[ascii_per_row:]
|
||||||
|
|
||||||
|
# Print by row
|
||||||
|
[printc(' '.join(line)) for line in zip(*current)]
|
||||||
|
print()
|
||||||
|
|
||||||
|
print('You can type "roll" to randomize again.')
|
||||||
|
print()
|
||||||
|
choice = literal_input(f'Your choice?', ['horizontal', 'vertical', 'roll'] + [f'random{i}' for i in range(random_count)], 'horizontal')
|
||||||
|
|
||||||
|
if choice == 'roll':
|
||||||
|
continue
|
||||||
|
|
||||||
|
if choice in ['horizontal', 'vertical']:
|
||||||
|
color_alignment = ColorAlignment(choice)
|
||||||
|
elif choice.startswith('random'):
|
||||||
|
color_alignment = ColorAlignment('custom', choices[int(choice[6])])
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
title += f'\n&e5. Color Alignment: &r{color_alignment}'
|
||||||
|
|
||||||
# Create config
|
# Create config
|
||||||
c = Config(preset, color_system)
|
clear_screen(title)
|
||||||
|
c = Config(preset, color_system, light_dark, lightness, color_alignment)
|
||||||
|
|
||||||
# Save config
|
# Save config
|
||||||
|
print()
|
||||||
save = literal_input(f'Save config?', ['y', 'n'], 'y')
|
save = literal_input(f'Save config?', ['y', 'n'], 'y')
|
||||||
if save == 'y':
|
if save == 'y':
|
||||||
c.save()
|
c.save()
|
||||||
@@ -132,7 +246,7 @@ def create_config() -> Config:
|
|||||||
|
|
||||||
def run():
|
def run():
|
||||||
# Create CLI
|
# Create CLI
|
||||||
hyfetch = color('&b&lhy&f&lfetch&r')
|
hyfetch = color('&b&lhyfetch&r')
|
||||||
parser = argparse.ArgumentParser(description=color(f'{hyfetch} - neofetch with flags <3'))
|
parser = argparse.ArgumentParser(description=color(f'{hyfetch} - neofetch with flags <3'))
|
||||||
|
|
||||||
parser.add_argument('-c', '--config', action='store_true', help=color(f'Configure {hyfetch}'))
|
parser.add_argument('-c', '--config', action='store_true', help=color(f'Configure {hyfetch}'))
|
||||||
@@ -140,9 +254,30 @@ def run():
|
|||||||
parser.add_argument('-m', '--mode', help=f'Color mode', choices=['8bit', 'rgb'])
|
parser.add_argument('-m', '--mode', help=f'Color mode', choices=['8bit', 'rgb'])
|
||||||
parser.add_argument('--c-scale', dest='scale', help=f'Lighten colors by a multiplier', type=float)
|
parser.add_argument('--c-scale', dest='scale', help=f'Lighten colors by a multiplier', type=float)
|
||||||
parser.add_argument('--c-set-l', dest='light', help=f'Set lightness value of the colors', type=float)
|
parser.add_argument('--c-set-l', dest='light', help=f'Set lightness value of the colors', type=float)
|
||||||
|
parser.add_argument('-V', '--version', dest='version', action='store_true', help=f'Check version')
|
||||||
|
parser.add_argument('--debug', action='store_true', help=color(f'Debug mode'))
|
||||||
|
parser.add_argument('--debug-list', help=color(f'Debug recommendations'))
|
||||||
|
parser.add_argument('--test-distro', help=color(f'Test for a specific distro'))
|
||||||
|
parser.add_argument('--test-print', action='store_true', help=color(f'Test print distro ascii art only'))
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.version:
|
||||||
|
print(f'Version is {VERSION}')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Test distro ascii art
|
||||||
|
if args.test_distro:
|
||||||
|
print(f'Setting distro to {args.test_distro}')
|
||||||
|
GLOBAL_CFG.override_distro = args.test_distro
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
GLOBAL_CFG.debug = True
|
||||||
|
|
||||||
|
if args.test_print:
|
||||||
|
print(get_distro_ascii())
|
||||||
|
return
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
config = check_config()
|
config = check_config()
|
||||||
|
|
||||||
@@ -156,13 +291,31 @@ def run():
|
|||||||
if args.mode:
|
if args.mode:
|
||||||
config.mode = args.mode
|
config.mode = args.mode
|
||||||
|
|
||||||
|
# Override global color mode
|
||||||
|
GLOBAL_CFG.color_mode = config.mode
|
||||||
|
GLOBAL_CFG.is_light = config.light_dark == 'light'
|
||||||
|
|
||||||
|
# Get preset
|
||||||
preset = PRESETS.get(config.preset)
|
preset = PRESETS.get(config.preset)
|
||||||
|
|
||||||
# Lighten
|
# Lighten
|
||||||
if args.scale:
|
if args.scale:
|
||||||
preset = ColorProfile([c.lighten(args.scale) for c in preset.colors])
|
preset = preset.lighten(args.scale)
|
||||||
if args.light:
|
if args.light:
|
||||||
preset = ColorProfile([c.set_light(args.light) for c in preset.colors])
|
preset = preset.set_light_raw(args.light)
|
||||||
|
if config.lightness:
|
||||||
|
preset = preset.set_light_dl(config.lightness)
|
||||||
|
|
||||||
|
# Debug recommendations
|
||||||
|
if args.debug_list:
|
||||||
|
distro = args.debug_list
|
||||||
|
ca = color_alignments[distro]
|
||||||
|
|
||||||
|
print(distro)
|
||||||
|
GLOBAL_CFG.override_distro = distro
|
||||||
|
asciis = [ca.recolor_ascii(get_distro_ascii(distro), p).split('\n') for p in list(PRESETS.values())[:3]]
|
||||||
|
[printc(' '.join(line)) for line in zip(*asciis)]
|
||||||
|
return
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
run_neofetch(preset, config.mode)
|
run_neofetch(preset, config.color_align)
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
from .color_util import AnsiMode, LightDark
|
||||||
|
from .constants import CONFIG_PATH
|
||||||
|
from .neofetch_util import ColorAlignment
|
||||||
|
from .serializer import json_stringify, from_dict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
preset: str
|
||||||
|
mode: AnsiMode
|
||||||
|
light_dark: LightDark = 'dark'
|
||||||
|
lightness: float | None = None
|
||||||
|
color_align: ColorAlignment = ColorAlignment('horizontal')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, d: dict):
|
||||||
|
d['color_align'] = ColorAlignment.from_dict(d['color_align'])
|
||||||
|
return from_dict(cls, d)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
CONFIG_PATH.write_text(json_stringify(self), 'utf-8')
|
||||||
+153
-17
@@ -1,15 +1,124 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from .color_util import AnsiMode
|
from hyfetch.color_util import color
|
||||||
|
from .constants import GLOBAL_CFG
|
||||||
from .presets import ColorProfile
|
from .presets import ColorProfile
|
||||||
|
from .serializer import from_dict
|
||||||
|
|
||||||
|
RE_NEOFETCH_COLOR = re.compile('\\${c[0-9]}')
|
||||||
|
|
||||||
|
|
||||||
|
def ascii_size(asc: str) -> tuple[int, int]:
|
||||||
|
"""
|
||||||
|
Get distro ascii width, height ignoring color code
|
||||||
|
|
||||||
|
:param asc: Distro ascii
|
||||||
|
:return: Width, Height
|
||||||
|
"""
|
||||||
|
return max(len(line) for line in re.sub(RE_NEOFETCH_COLOR, '', asc).split('\n')), len(asc.split('\n'))
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_ascii(asc: str) -> str:
|
||||||
|
"""
|
||||||
|
Make sure every line are the same width
|
||||||
|
"""
|
||||||
|
w = ascii_size(asc)[0]
|
||||||
|
return '\n'.join(line + ' ' * (w - ascii_size(line)[0]) for line in asc.split('\n'))
|
||||||
|
|
||||||
|
|
||||||
|
def fill_starting(asc: str) -> str:
|
||||||
|
"""
|
||||||
|
Fill the missing starting placeholders.
|
||||||
|
|
||||||
|
E.g. "${c1}...\n..." -> "${c1}...\n${c1}..."
|
||||||
|
"""
|
||||||
|
new = []
|
||||||
|
last = ''
|
||||||
|
for line in asc.split('\n'):
|
||||||
|
new.append(last + line)
|
||||||
|
|
||||||
|
# Line has color placeholders
|
||||||
|
matches = RE_NEOFETCH_COLOR.findall(line)
|
||||||
|
if len(matches) > 0:
|
||||||
|
# Get the last placeholder for the next line
|
||||||
|
last = matches[-1]
|
||||||
|
|
||||||
|
return '\n'.join(new)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ColorAlignment:
|
||||||
|
mode: Literal['horizontal', 'vertical', 'custom']
|
||||||
|
|
||||||
|
# custom_colors[ascii color index] = unique color index in preset
|
||||||
|
custom_colors: dict[int, int] = ()
|
||||||
|
|
||||||
|
# Foreground/background ascii color index
|
||||||
|
fore_back: tuple[int, int] = ()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, d: dict):
|
||||||
|
return from_dict(cls, d)
|
||||||
|
|
||||||
|
def recolor_ascii(self, asc: str, preset: ColorProfile) -> str:
|
||||||
|
"""
|
||||||
|
Use the color alignment to recolor an ascii art
|
||||||
|
|
||||||
|
:return Colored ascii, Uncolored lines
|
||||||
|
"""
|
||||||
|
asc = fill_starting(asc)
|
||||||
|
|
||||||
|
if self.fore_back and self.mode in ['horizontal', 'vertical']:
|
||||||
|
fore, back = self.fore_back
|
||||||
|
|
||||||
|
# Replace foreground colors
|
||||||
|
asc = asc.replace(f'${{c{fore}}}', color('&0' if GLOBAL_CFG.is_light else '&f'))
|
||||||
|
lines = asc.split('\n')
|
||||||
|
|
||||||
|
# Add new colors
|
||||||
|
if self.mode == 'horizontal':
|
||||||
|
colors = preset.with_length(len(lines))
|
||||||
|
asc = '\n'.join([l.replace(f'${{c{back}}}', colors[i].to_ansi()) + color('&r') for i, l in enumerate(lines)])
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
# Remove existing colors
|
||||||
|
asc = re.sub(RE_NEOFETCH_COLOR, '', asc)
|
||||||
|
|
||||||
|
elif self.mode in ['horizontal', 'vertical']:
|
||||||
|
# Remove existing colors
|
||||||
|
asc = re.sub(RE_NEOFETCH_COLOR, '', asc)
|
||||||
|
lines = asc.split('\n')
|
||||||
|
|
||||||
|
# Add new colors
|
||||||
|
if self.mode == 'horizontal':
|
||||||
|
colors = preset.with_length(len(lines))
|
||||||
|
asc = '\n'.join([colors[i].to_ansi() + l + color('&r') for i, l in enumerate(lines)])
|
||||||
|
else:
|
||||||
|
asc = '\n'.join(preset.color_text(line) + color('&r') for line in lines)
|
||||||
|
|
||||||
|
else:
|
||||||
|
preset = preset.unique_colors()
|
||||||
|
|
||||||
|
# Apply colors
|
||||||
|
color_map = {ai: preset.colors[pi].to_ansi() for ai, pi in self.custom_colors.items()}
|
||||||
|
for ascii_i, c in color_map.items():
|
||||||
|
asc = asc.replace(f'${{c{ascii_i}}}', c)
|
||||||
|
|
||||||
|
return asc
|
||||||
|
|
||||||
|
|
||||||
def get_command_path() -> str:
|
def get_command_path() -> str:
|
||||||
@@ -21,26 +130,30 @@ def get_command_path() -> str:
|
|||||||
return pkg_resources.resource_filename(__name__, 'scripts/neofetch_mod.sh')
|
return pkg_resources.resource_filename(__name__, 'scripts/neofetch_mod.sh')
|
||||||
|
|
||||||
|
|
||||||
def get_distro_ascii() -> str:
|
def get_distro_ascii(distro: str | None = None) -> str:
|
||||||
"""
|
"""
|
||||||
Get the distro ascii
|
Get the distro ascii of the current distro. Or if distro is specified, get the specific distro's
|
||||||
|
ascii art instead.
|
||||||
|
|
||||||
:return: Distro ascii
|
:return: Distro ascii
|
||||||
"""
|
"""
|
||||||
return check_output([get_command_path(), "print_ascii"]).decode().strip()
|
if not distro and GLOBAL_CFG.override_distro:
|
||||||
|
distro = GLOBAL_CFG.override_distro
|
||||||
|
if GLOBAL_CFG.debug:
|
||||||
|
print(distro)
|
||||||
|
print(GLOBAL_CFG)
|
||||||
|
cmd = 'print_ascii'
|
||||||
|
if distro:
|
||||||
|
os.environ['CUSTOM_DISTRO'] = distro
|
||||||
|
cmd = 'print_custom_ascii'
|
||||||
|
|
||||||
|
return normalize_ascii(check_output([get_command_path(), cmd]).decode().strip())
|
||||||
|
|
||||||
|
|
||||||
def run_neofetch(preset: ColorProfile, mode: AnsiMode):
|
def run_neofetch(preset: ColorProfile, alignment: ColorAlignment):
|
||||||
# Get existing ascii
|
|
||||||
asc = get_distro_ascii()
|
asc = get_distro_ascii()
|
||||||
|
w, h = ascii_size(asc)
|
||||||
# Remove existing colors
|
asc = alignment.recolor_ascii(asc, preset)
|
||||||
asc = re.sub('\\${.*?}', '', asc)
|
|
||||||
|
|
||||||
# Add new colors
|
|
||||||
lines = asc.split('\n')
|
|
||||||
colors = preset.with_length(len(lines))
|
|
||||||
asc = '\n'.join([colors[i].to_ansi(mode) + l for i, l in enumerate(lines)])
|
|
||||||
|
|
||||||
# Write temp file
|
# Write temp file
|
||||||
with TemporaryDirectory() as tmp_dir:
|
with TemporaryDirectory() as tmp_dir:
|
||||||
@@ -49,6 +162,29 @@ def run_neofetch(preset: ColorProfile, mode: AnsiMode):
|
|||||||
path.write_text(asc)
|
path.write_text(asc)
|
||||||
|
|
||||||
# Call neofetch with the temp file
|
# Call neofetch with the temp file
|
||||||
os.environ['ascii_len'] = str(max(len(l) for l in lines))
|
os.environ['ascii_len'] = str(w)
|
||||||
os.environ['ascii_lines'] = str(len(lines))
|
os.environ['ascii_lines'] = str(h)
|
||||||
os.system(get_command_path() + f' --ascii --source {path.absolute()} --ascii-colors')
|
|
||||||
|
if platform.system() != 'Windows':
|
||||||
|
os.system(f'{get_command_path()} --ascii --source {path.absolute()} --ascii-colors')
|
||||||
|
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
cmd = get_command_path().replace("\\", "/").replace("C:/", "/c/")
|
||||||
|
path_str = str(path.absolute()).replace('\\', '/').replace('C:/', '/c/')
|
||||||
|
|
||||||
|
cmd = f'ascii_len={w} ascii_lines={h} {cmd} --ascii --source {path_str} --ascii-colors'
|
||||||
|
full_cmd = ['C:\\Program Files\\Git\\bin\\bash.exe', '-c', cmd]
|
||||||
|
# print(full_cmd)
|
||||||
|
|
||||||
|
subprocess.run(full_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
# Color alignment recommendations
|
||||||
|
color_alignments = {
|
||||||
|
'fedora': ColorAlignment('horizontal', fore_back=(2, 1)),
|
||||||
|
'ubuntu': ColorAlignment('horizontal', fore_back=(2, 1)),
|
||||||
|
'NixOS.*': ColorAlignment('custom', {1: 1, 2: 0}),
|
||||||
|
# 'arch': ColorAlignment('horizontal'),
|
||||||
|
# 'centos': ColorAlignment('horizontal'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+371
-3
@@ -1,8 +1,20 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from .color_util import RGB
|
from .color_util import RGB, LightDark
|
||||||
|
from .constants import GLOBAL_CFG
|
||||||
|
|
||||||
|
|
||||||
|
def remove_duplicates(seq: Iterable) -> list:
|
||||||
|
"""
|
||||||
|
Remove duplicate items from a sequence while preserving the order
|
||||||
|
"""
|
||||||
|
seen = set()
|
||||||
|
seen_add = seen.add
|
||||||
|
return [x for x in seq if not (x in seen or seen_add(x))]
|
||||||
|
|
||||||
|
|
||||||
class ColorProfile:
|
class ColorProfile:
|
||||||
@@ -17,7 +29,6 @@ class ColorProfile:
|
|||||||
else:
|
else:
|
||||||
self.colors = colors
|
self.colors = colors
|
||||||
|
|
||||||
|
|
||||||
def with_weights(self, weights: list[int]) -> list[RGB]:
|
def with_weights(self, weights: list[int]) -> list[RGB]:
|
||||||
"""
|
"""
|
||||||
Map colors based on weights
|
Map colors based on weights
|
||||||
@@ -76,11 +87,58 @@ class ColorProfile:
|
|||||||
result += '\033[0m'
|
result += '\033[0m'
|
||||||
result += t
|
result += t
|
||||||
else:
|
else:
|
||||||
result += colors[i].to_ansi_rgb(foreground) + t
|
result += colors[i].to_ansi(foreground=foreground) + t
|
||||||
|
|
||||||
result += '\033[0m'
|
result += '\033[0m'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def lighten(self, multiplier: float) -> ColorProfile:
|
||||||
|
"""
|
||||||
|
Lighten the color profile by a multiplier
|
||||||
|
|
||||||
|
:param multiplier: Multiplier
|
||||||
|
:return: Lightened color profile (original isn't modified)
|
||||||
|
"""
|
||||||
|
return ColorProfile([c.lighten(multiplier) for c in self.colors])
|
||||||
|
|
||||||
|
def set_light_raw(self, light: float, at_least: bool | None = None, at_most: bool | None = None) -> 'ColorProfile':
|
||||||
|
"""
|
||||||
|
Set HSL lightness value
|
||||||
|
|
||||||
|
:param light: Lightness value (0-1)
|
||||||
|
:param at_least: Set the lightness to at least this value (no change if greater)
|
||||||
|
:param at_most: Set the lightness to at most this value (no change if lesser)
|
||||||
|
:return: New color profile (original isn't modified)
|
||||||
|
"""
|
||||||
|
return ColorProfile([c.set_light(light, at_least, at_most) for c in self.colors])
|
||||||
|
|
||||||
|
def set_light_dl(self, light: float, term: LightDark = GLOBAL_CFG.light_dark()):
|
||||||
|
"""
|
||||||
|
Set HSL lightness value with respect to dark/light terminals
|
||||||
|
|
||||||
|
:param light: Lightness value (0-1)
|
||||||
|
:param term: Terminal color (can be "dark" or "light")
|
||||||
|
:return: New color profile (original isn't modified)
|
||||||
|
"""
|
||||||
|
assert term.lower() in ['light', 'dark']
|
||||||
|
at_least, at_most = (True, None) if term.lower() == 'dark' else (None, True)
|
||||||
|
return self.set_light_raw(light, at_least, at_most)
|
||||||
|
|
||||||
|
def set_light_dl_def(self, term: LightDark | None = None):
|
||||||
|
"""
|
||||||
|
Set default lightness with respect to dark/light terminals
|
||||||
|
|
||||||
|
:param term: Terminal color (can be "dark" or "light")
|
||||||
|
:return: New color profile (original isn't modified)
|
||||||
|
"""
|
||||||
|
return self.set_light_dl(GLOBAL_CFG.default_lightness(term), term)
|
||||||
|
|
||||||
|
def unique_colors(self) -> ColorProfile:
|
||||||
|
"""
|
||||||
|
Create another color profile with only the unique colors
|
||||||
|
"""
|
||||||
|
return ColorProfile(remove_duplicates(self.colors))
|
||||||
|
|
||||||
|
|
||||||
PRESETS: dict[str, ColorProfile] = {
|
PRESETS: dict[str, ColorProfile] = {
|
||||||
'rainbow': ColorProfile([
|
'rainbow': ColorProfile([
|
||||||
@@ -99,12 +157,14 @@ PRESETS: dict[str, ColorProfile] = {
|
|||||||
'#F6AAB7',
|
'#F6AAB7',
|
||||||
'#55CDFD'
|
'#55CDFD'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
'nonbinary': ColorProfile([
|
'nonbinary': ColorProfile([
|
||||||
'#FCF431',
|
'#FCF431',
|
||||||
'#FCFCFC',
|
'#FCFCFC',
|
||||||
'#9D59D2',
|
'#9D59D2',
|
||||||
'#282828'
|
'#282828'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
'agender': ColorProfile([
|
'agender': ColorProfile([
|
||||||
'#000000',
|
'#000000',
|
||||||
'#BABABA',
|
'#BABABA',
|
||||||
@@ -114,11 +174,13 @@ PRESETS: dict[str, ColorProfile] = {
|
|||||||
'#BABABA',
|
'#BABABA',
|
||||||
'#000000'
|
'#000000'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
'queer': ColorProfile([
|
'queer': ColorProfile([
|
||||||
'#B57FDD',
|
'#B57FDD',
|
||||||
'#FFFFFF',
|
'#FFFFFF',
|
||||||
'#49821E'
|
'#49821E'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
'genderfluid': ColorProfile([
|
'genderfluid': ColorProfile([
|
||||||
'#FE76A2',
|
'#FE76A2',
|
||||||
'#FFFFFF',
|
'#FFFFFF',
|
||||||
@@ -132,11 +194,37 @@ PRESETS: dict[str, ColorProfile] = {
|
|||||||
'#9B4F96',
|
'#9B4F96',
|
||||||
'#0038A8'
|
'#0038A8'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
'pansexual': ColorProfile([
|
'pansexual': ColorProfile([
|
||||||
'#FF1C8D',
|
'#FF1C8D',
|
||||||
'#FFD700',
|
'#FFD700',
|
||||||
'#1AB3FF'
|
'#1AB3FF'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
'polysexual': ColorProfile([
|
||||||
|
'#F714BA',
|
||||||
|
'#01D66A',
|
||||||
|
'#1594F6',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# omnisexual sorced from https://www.flagcolorcodes.com/omnisexual
|
||||||
|
'omnisexual': ColorProfile([
|
||||||
|
'#FE9ACE',
|
||||||
|
'#FF53BF',
|
||||||
|
'#200044',
|
||||||
|
'#6760FE',
|
||||||
|
'#8EA6FF',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# gay men sourced from https://www.flagcolorcodes.com/gay-men
|
||||||
|
'gay-men': ColorProfile([
|
||||||
|
'#078D70',
|
||||||
|
'#98E8C1',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#7BADE2',
|
||||||
|
'#3D1A78'
|
||||||
|
]),
|
||||||
|
|
||||||
'lesbian': ColorProfile([
|
'lesbian': ColorProfile([
|
||||||
'#D62800',
|
'#D62800',
|
||||||
'#FF9B56',
|
'#FF9B56',
|
||||||
@@ -144,12 +232,24 @@ PRESETS: dict[str, ColorProfile] = {
|
|||||||
'#D462A6',
|
'#D462A6',
|
||||||
'#A40062'
|
'#A40062'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
# abrosexual used colorpicker to source from
|
||||||
|
# https://fyeahaltpride.tumblr.com/post/151704251345/could-you-guys-possibly-make-an-abrosexual-pride
|
||||||
|
'abrosexual': ColorProfile([
|
||||||
|
'#46D294',
|
||||||
|
'#A3E9CA',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#F78BB3',
|
||||||
|
'#EE1766',
|
||||||
|
]),
|
||||||
|
|
||||||
'asexual': ColorProfile([
|
'asexual': ColorProfile([
|
||||||
'#000000',
|
'#000000',
|
||||||
'#A4A4A4',
|
'#A4A4A4',
|
||||||
'#FFFFFF',
|
'#FFFFFF',
|
||||||
'#810081'
|
'#810081'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
'aromantic': ColorProfile([
|
'aromantic': ColorProfile([
|
||||||
'#3BA740',
|
'#3BA740',
|
||||||
'#A8D47A',
|
'#A8D47A',
|
||||||
@@ -157,4 +257,272 @@ PRESETS: dict[str, ColorProfile] = {
|
|||||||
'#ABABAB',
|
'#ABABAB',
|
||||||
'#000000'
|
'#000000'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
# aroace1 sourced from https://flag.library.lgbt/flags/aroace/
|
||||||
|
'aroace1': ColorProfile([
|
||||||
|
'#E28C00',
|
||||||
|
'#ECCD00',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#62AEDC',
|
||||||
|
'#203856'
|
||||||
|
]),
|
||||||
|
|
||||||
|
'aroace2': ColorProfile([
|
||||||
|
'#000000',
|
||||||
|
'#810081',
|
||||||
|
'#A4A4A4',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#A8D47A',
|
||||||
|
'#3BA740'
|
||||||
|
]),
|
||||||
|
|
||||||
|
'aroace3': ColorProfile([
|
||||||
|
'#3BA740',
|
||||||
|
'#A8D47A',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#ABABAB',
|
||||||
|
'#000000',
|
||||||
|
'#A4A4A4',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#810081'
|
||||||
|
]),
|
||||||
|
|
||||||
|
# below sourced from https://www.flagcolorcodes.com/flags/pride
|
||||||
|
# goto f"https://www.flagcolorcodes.com/{preset}" for info
|
||||||
|
# todo: sane sorting
|
||||||
|
'autosexual': ColorProfile([
|
||||||
|
'#99D9EA',
|
||||||
|
'#7F7F7F'
|
||||||
|
]),
|
||||||
|
|
||||||
|
'intergender': ColorProfile([
|
||||||
|
# todo: use weighted spacing
|
||||||
|
'#900DC2',
|
||||||
|
'#900DC2',
|
||||||
|
'#FFE54F',
|
||||||
|
'#900DC2',
|
||||||
|
'#900DC2',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'greygender': ColorProfile([
|
||||||
|
'#B3B3B3',
|
||||||
|
'#B3B3B3',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#062383',
|
||||||
|
'#062383',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#535353',
|
||||||
|
'#535353',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'akiosexual': ColorProfile([
|
||||||
|
'#F9485E',
|
||||||
|
'#FEA06A',
|
||||||
|
'#FEF44C',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#000000',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# bigender sourced from https://www.flagcolorcodes.com/bigender
|
||||||
|
'bigender': ColorProfile([
|
||||||
|
'#C479A2',
|
||||||
|
'#EDA5CD',
|
||||||
|
'#D6C7E8',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#D6C7E8',
|
||||||
|
'#9AC7E8',
|
||||||
|
'#6D82D1',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# demigender yellow sourced from https://lgbtqia.fandom.com/f/p/4400000000000041031
|
||||||
|
# other colors sourced from demiboy and demigirl flags
|
||||||
|
'demigender': ColorProfile([
|
||||||
|
'#7F7F7F',
|
||||||
|
'#C4C4C4',
|
||||||
|
'#FBFF75',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#FBFF75',
|
||||||
|
'#C4C4C4',
|
||||||
|
'#7F7F7F',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# demiboy sourced from https://www.flagcolorcodes.com/demiboy
|
||||||
|
'demiboy': ColorProfile([
|
||||||
|
'#7F7F7F',
|
||||||
|
'#C4C4C4',
|
||||||
|
'#9DD7EA',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#9DD7EA',
|
||||||
|
'#C4C4C4',
|
||||||
|
'#7F7F7F',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# demigirl sourced from https://www.flagcolorcodes.com/demigirl
|
||||||
|
'demigirl': ColorProfile([
|
||||||
|
'#7F7F7F',
|
||||||
|
'#C4C4C4',
|
||||||
|
'#FDADC8',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#FDADC8',
|
||||||
|
'#C4C4C4',
|
||||||
|
'#7F7F7F',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'transmasculine': ColorProfile([
|
||||||
|
'#FF8ABD',
|
||||||
|
'#CDF5FE',
|
||||||
|
'#9AEBFF',
|
||||||
|
'#74DFFF',
|
||||||
|
'#9AEBFF',
|
||||||
|
'#CDF5FE',
|
||||||
|
'#FF8ABD',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# transfeminine used colorpicker to source from https://www.deviantart.com/pride-flags/art/Trans-Woman-Transfeminine-1-543925985
|
||||||
|
# linked from https://gender.fandom.com/wiki/Transfeminine
|
||||||
|
'transfeminine': ColorProfile([
|
||||||
|
'#73DEFF',
|
||||||
|
'#FFE2EE',
|
||||||
|
'#FFB5D6',
|
||||||
|
'#FF8DC0',
|
||||||
|
'#FFB5D6',
|
||||||
|
'#FFE2EE',
|
||||||
|
'#73DEFF',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# genderfaun sourced from https://www.flagcolorcodes.com/genderfaun
|
||||||
|
'genderfaun': ColorProfile([
|
||||||
|
'#FCD689',
|
||||||
|
'#FFF09B',
|
||||||
|
'#FAF9CD',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#8EDED9',
|
||||||
|
'#8CACDE',
|
||||||
|
'#9782EC',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'demifaun': ColorProfile([
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#C6C6C6',
|
||||||
|
'#C6C6C6',
|
||||||
|
'#FCC688',
|
||||||
|
'#FFF19C',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#8DE0D5',
|
||||||
|
'#9682EC',
|
||||||
|
'#C6C6C6',
|
||||||
|
'#C6C6C6',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# genderfae sourced from https://www.flagcolorcodes.com/genderfae
|
||||||
|
'genderfae': ColorProfile([
|
||||||
|
'#97C3A5',
|
||||||
|
'#C3DEAE',
|
||||||
|
'#F9FACD',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#FCA2C4',
|
||||||
|
'#DB8AE4',
|
||||||
|
'#A97EDD',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# demifae used colorpicker to source form https://www.deviantart.com/pride-flags/art/Demifae-870194777
|
||||||
|
'demifae': ColorProfile([
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#C5C5C5',
|
||||||
|
'#C5C5C5',
|
||||||
|
'#97C3A4',
|
||||||
|
'#C4DEAE',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#FCA2C5',
|
||||||
|
'#AB7EDF',
|
||||||
|
'#C5C5C5',
|
||||||
|
'#C5C5C5',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'neutrois': ColorProfile([
|
||||||
|
'#FFFFFF',
|
||||||
|
'#1F9F00',
|
||||||
|
'#000000'
|
||||||
|
]),
|
||||||
|
|
||||||
|
'biromantic1': ColorProfile([
|
||||||
|
'#8869A5',
|
||||||
|
'#D8A7D8',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#FDB18D',
|
||||||
|
'#151638',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'biromantic2': ColorProfile([
|
||||||
|
'#740194',
|
||||||
|
'#AEB1AA',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#AEB1AA',
|
||||||
|
'#740194',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'autoromantic': ColorProfile([ # symbol interpreted
|
||||||
|
'#99D9EA',
|
||||||
|
'#99D9EA',
|
||||||
|
'#99D9EA',
|
||||||
|
'#99D9EA',
|
||||||
|
'#99D9EA',
|
||||||
|
'#000000',
|
||||||
|
'#3DA542',
|
||||||
|
'#3DA542',
|
||||||
|
'#000000',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
'#7F7F7F',
|
||||||
|
]),
|
||||||
|
|
||||||
|
# i didn't expect this one to work. cool!
|
||||||
|
'boyflux2': ColorProfile([
|
||||||
|
'#E48AE4',
|
||||||
|
'#9A81B4',
|
||||||
|
'#55BFAB',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#A8A8A8',
|
||||||
|
'#81D5EF',
|
||||||
|
'#81D5EF',
|
||||||
|
'#81D5EF',
|
||||||
|
'#81D5EF',
|
||||||
|
'#81D5EF',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#69ABE5',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
'#5276D4',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'beiyang': ColorProfile([
|
||||||
|
'#DF1B12',
|
||||||
|
'#FFC600',
|
||||||
|
'#01639D',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#000000',
|
||||||
|
]),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import inspect
|
||||||
|
import json
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
|
|
||||||
|
class EnhancedJSONEncoder(json.JSONEncoder):
|
||||||
|
"""
|
||||||
|
An improvement to the json.JSONEncoder class, which supports:
|
||||||
|
encoding for dataclasses, encoding for datetime, and sets
|
||||||
|
"""
|
||||||
|
|
||||||
|
def default(self, o: object) -> object:
|
||||||
|
|
||||||
|
# Support encoding dataclasses
|
||||||
|
# https://stackoverflow.com/a/51286749/7346633
|
||||||
|
if dataclasses.is_dataclass(o):
|
||||||
|
return dataclasses.asdict(o)
|
||||||
|
|
||||||
|
# Support encoding datetime
|
||||||
|
if isinstance(o, (datetime, date)):
|
||||||
|
return o.isoformat()
|
||||||
|
|
||||||
|
# Support for sets
|
||||||
|
# https://stackoverflow.com/a/8230505/7346633
|
||||||
|
if isinstance(o, set):
|
||||||
|
return list(o)
|
||||||
|
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
|
def json_stringify(obj: object, indent: int | None = None) -> str:
|
||||||
|
"""
|
||||||
|
Serialize json string with support for dataclasses and datetime and sets and with custom
|
||||||
|
configuration.
|
||||||
|
Preconditions:
|
||||||
|
- obj != None
|
||||||
|
:param obj: Objects
|
||||||
|
:param indent: Indent size or none
|
||||||
|
:return: Json strings
|
||||||
|
"""
|
||||||
|
return json.dumps(obj, indent=indent, cls=EnhancedJSONEncoder, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
def from_dict(cls, d: dict):
|
||||||
|
return cls(**{k: v for k, v in d.items() if k in inspect.signature(cls).parameters})
|
||||||
@@ -1677,8 +1677,12 @@ get_packages() {
|
|||||||
has scoop && pkgs_h=1 dir ~/scoop/apps/* && ((packages-=1))
|
has scoop && pkgs_h=1 dir ~/scoop/apps/* && ((packages-=1))
|
||||||
|
|
||||||
# Count chocolatey packages.
|
# Count chocolatey packages.
|
||||||
[[ -d /cygdrive/c/ProgramData/chocolatey/lib ]] && \
|
# [[ -d /c/ProgramData/chocolatey/lib ]] && \
|
||||||
dir /cygdrive/c/ProgramData/chocolatey/lib/*
|
# dir /c/ProgramData/chocolatey/lib/*
|
||||||
|
has choco && tot choco list --localonly
|
||||||
|
|
||||||
|
# Count winget
|
||||||
|
has winget && tot winget list
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Haiku)
|
Haiku)
|
||||||
@@ -3315,7 +3319,7 @@ get_term() {
|
|||||||
name="$(get_process_name "$parent")"
|
name="$(get_process_name "$parent")"
|
||||||
|
|
||||||
case ${name// } in
|
case ${name// } in
|
||||||
"${SHELL/*\/}"|*"sh"|"screen"|"su"*|"newgrp") ;;
|
"${SHELL/*\/}"|*"sh"|"screen"|"su"*|"newgrp"|"hyfetch") ;;
|
||||||
|
|
||||||
"login"*|*"Login"*|"init"|"(init)")
|
"login"*|*"Login"*|"init"|"(init)")
|
||||||
term="$(tty)"
|
term="$(tty)"
|
||||||
@@ -11594,6 +11598,11 @@ main() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_ascii_distro_name() {
|
||||||
|
get_distro
|
||||||
|
echo "$ascii_distro"
|
||||||
|
}
|
||||||
|
|
||||||
get_print_ascii() {
|
get_print_ascii() {
|
||||||
cache_uname
|
cache_uname
|
||||||
get_os
|
get_os
|
||||||
@@ -11603,4 +11612,24 @@ get_print_ascii() {
|
|||||||
echo "$ascii_data"
|
echo "$ascii_data"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_print_custom_ascii() {
|
||||||
|
distro="$CUSTOM_DISTRO"
|
||||||
|
ascii_distro=$distro
|
||||||
|
get_distro_ascii
|
||||||
|
echo "$ascii_data"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_test_distro_ascii() {
|
||||||
|
# Load default config.
|
||||||
|
eval "$config"
|
||||||
|
|
||||||
|
distro="$CUSTOM_DISTRO"
|
||||||
|
ascii_distro=$distro
|
||||||
|
get_bold
|
||||||
|
get_distro_ascii
|
||||||
|
|
||||||
|
image_backend
|
||||||
|
dynamic_prompt
|
||||||
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import hyfetch
|
import hyfetch
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ setup(
|
|||||||
packages=['hyfetch'],
|
packages=['hyfetch'],
|
||||||
package_data={'hyfetch': ['hyfetch/*']},
|
package_data={'hyfetch': ['hyfetch/*']},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['hypy_utils==1.0.6', 'typing_extensions'],
|
install_requires=['setuptools', 'typing_extensions'],
|
||||||
entry_points={
|
entry_points={
|
||||||
"console_scripts": [
|
"console_scripts": [
|
||||||
"hyfetch=hyfetch.main:run",
|
"hyfetch=hyfetch.main:run",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from hypy_utils import printc
|
from hyfetch.color_scale import test_color_scale
|
||||||
|
from hyfetch.color_util import RGB, printc
|
||||||
from hyfetch.color_util import RGB
|
|
||||||
from hyfetch.neofetch_util import get_command_path, run_neofetch
|
from hyfetch.neofetch_util import get_command_path, run_neofetch
|
||||||
from hyfetch.presets import PRESETS
|
from hyfetch.presets import PRESETS
|
||||||
|
|
||||||
@@ -35,6 +34,6 @@ def test_rgb_8bit_conversion():
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_rgb_8bit_conversion()
|
test_rgb_8bit_conversion()
|
||||||
|
test_color_scale()
|
||||||
|
|||||||
Reference in New Issue
Block a user