Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb76b2f474 | |||
| c9ed8e2088 | |||
| b68e0ccf52 | |||
| 143bfd2db7 | |||
| 2e20d4e529 | |||
| 66ff8d06d9 | |||
| 887f766464 | |||
| 85eaad14f2 | |||
| 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 | |||
| b572e5d35f | |||
| 86af5598c6 | |||
| d7273909f6 | |||
| 7b4f209ebe | |||
| 46d00666e2 | |||
| 1cffe3556a | |||
| 07d94eec8b | |||
| 62469e6f52 |
@@ -19,8 +19,11 @@ pip install hyfetch
|
||||
Currently, these distributions have existing packages for HyFetch:
|
||||
|
||||
* ArchLinux: `yay -S hyfetch` (Thanks to @ Aleksana)
|
||||
* NixOS: `nix-env -i hyfetch` ([In Progress](https://github.com/NixOS/nixpkgs/pull/170309))
|
||||
* Guix: [In progress](https://issues.guix.gnu.org/54847#8-lineno27)
|
||||
|
||||
Currently, if you're using NixOS, you can use HyFetch with `nix-env -if https://github.com/hykilpikonna/hyfetch/tarball/master -A hyfetch`
|
||||
|
||||
## 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!
|
||||
|
||||
+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,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
from . import main
|
||||
|
||||
|
||||
__version__ = main.VERSION
|
||||
|
||||
|
||||
|
||||
@@ -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=' ')
|
||||
+39
-6
@@ -1,10 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import colorsys
|
||||
from typing import NamedTuple
|
||||
from typing import NamedTuple, Callable, Optional
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
from .constants import GLOBAL_CFG
|
||||
|
||||
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",
|
||||
@@ -36,7 +40,7 @@ def color(msg: str) -> str:
|
||||
code = code.replace(',', ' ').replace(';', ' ').replace(' ', ' ')
|
||||
rgb = tuple(int(c) for c in code.split(' '))
|
||||
|
||||
msg = msg[:i] + RGB(*rgb).to_ansi_rgb(foreground=fore) + msg[end + 1:]
|
||||
msg = msg[:i] + RGB(*rgb).to_ansi(foreground=fore) + msg[end + 1:]
|
||||
|
||||
return msg
|
||||
|
||||
@@ -49,6 +53,19 @@ def printc(msg: str):
|
||||
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]:
|
||||
"""
|
||||
Redistribute RGB after lightening
|
||||
@@ -141,7 +158,9 @@ class RGB(NamedTuple):
|
||||
"""
|
||||
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':
|
||||
return self.to_ansi_rgb(foreground)
|
||||
if mode == '8bit':
|
||||
@@ -158,12 +177,26 @@ class RGB(NamedTuple):
|
||||
"""
|
||||
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
|
||||
|
||||
: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)
|
||||
"""
|
||||
# Convert to HSL
|
||||
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.0'
|
||||
|
||||
# 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)
|
||||
+211
-66
@@ -3,27 +3,18 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
import random
|
||||
import re
|
||||
from itertools import permutations
|
||||
from typing import Iterable
|
||||
|
||||
from .color_util import AnsiMode, printc, color
|
||||
from .neofetch_util import run_neofetch
|
||||
from .presets import PRESETS, ColorProfile
|
||||
from .serializer import json_stringify
|
||||
from hyfetch import presets
|
||||
|
||||
CONFIG_PATH = Path.home() / '.config/hyfetch.json'
|
||||
VERSION = '1.0.7'
|
||||
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
preset: str
|
||||
mode: AnsiMode
|
||||
|
||||
def save(self):
|
||||
CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True)
|
||||
CONFIG_PATH.write_text(json_stringify(self), 'utf-8')
|
||||
from .color_util import printc, color, clear_screen, LightDark
|
||||
from .constants import CONFIG_PATH, VERSION, TERM_LEN, TEST_ASCII_WIDTH, TEST_ASCII, GLOBAL_CFG
|
||||
from .models import Config
|
||||
from .neofetch_util import run_neofetch, get_distro_ascii, ColorAlignment, ascii_size, color_alignments
|
||||
from .presets import PRESETS
|
||||
|
||||
|
||||
def check_config() -> Config:
|
||||
@@ -36,25 +27,30 @@ def check_config() -> Config:
|
||||
:return: Config object
|
||||
"""
|
||||
if CONFIG_PATH.is_file():
|
||||
return Config(**json.loads(CONFIG_PATH.read_text('utf-8')))
|
||||
return Config.from_dict(json.loads(CONFIG_PATH.read_text('utf-8')))
|
||||
|
||||
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
|
||||
|
||||
:param prompt: Input prompt
|
||||
:param options: Options
|
||||
:param default: Default option
|
||||
:param show_ops: Show options
|
||||
:return: Selection
|
||||
"""
|
||||
options = list(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])
|
||||
printc(f'{prompt} ({op_text})')
|
||||
if show_ops:
|
||||
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
|
||||
while not selection.lower() in lows:
|
||||
print(f'Invalid selection! {selection} is not one of {"|".join(options)}')
|
||||
@@ -64,65 +60,180 @@ def literal_input(prompt: str, options: Iterable[str], default: str) -> str:
|
||||
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:
|
||||
"""
|
||||
Create config interactively
|
||||
|
||||
:return: Config object (automatically stored)
|
||||
"""
|
||||
# Select color system
|
||||
# TODO: Demo of each color system
|
||||
color_system = literal_input('Which &acolor &bsystem &rdo you want to use?',
|
||||
['8bit', 'rgb'], 'rgb')
|
||||
title = 'Welcome to &b&lhy&f&lfetch&r! Let\'s set up some colors first.'
|
||||
clear_screen(title)
|
||||
|
||||
# Print preset
|
||||
print('Available presets:\n')
|
||||
spacing = max(max(len(k) for k in PRESETS.keys()), 30)
|
||||
##############################
|
||||
# 1. Select color system
|
||||
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 = []
|
||||
spacing = max(max(len(k) for k in PRESETS.keys()), 20)
|
||||
for name, preset in PRESETS.items():
|
||||
flags.append([preset.color_text(' ' * spacing, foreground=False),
|
||||
'&0' + preset.color_text(center_text(name, spacing), foreground=False),
|
||||
preset.color_text(' ' * spacing, foreground=False)])
|
||||
flags_per_row = 3
|
||||
flag = preset.color_text(' ' * spacing, foreground=False)
|
||||
flags.append([name.center(spacing), flag, flag, flag])
|
||||
|
||||
# Calculate flags per row
|
||||
flags_per_row = TERM_LEN // (spacing + 2)
|
||||
while flags:
|
||||
current = 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()
|
||||
tmp = PRESETS['rainbow'].color_text('preset')
|
||||
preset = literal_input(f'Which {tmp} do you want to use?', PRESETS.keys(), 'rainbow')
|
||||
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', 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
|
||||
c = Config(preset, color_system)
|
||||
clear_screen(title)
|
||||
c = Config(preset, color_system, light_dark, lightness, color_alignment)
|
||||
|
||||
# Save config
|
||||
print()
|
||||
save = literal_input(f'Save config?', ['y', 'n'], 'y')
|
||||
if save == 'y':
|
||||
c.save()
|
||||
@@ -132,7 +243,7 @@ def create_config() -> Config:
|
||||
|
||||
def run():
|
||||
# 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.add_argument('-c', '--config', action='store_true', help=color(f'Configure {hyfetch}'))
|
||||
@@ -141,6 +252,10 @@ def run():
|
||||
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('-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()
|
||||
|
||||
@@ -148,6 +263,18 @@ def run():
|
||||
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
|
||||
config = check_config()
|
||||
|
||||
@@ -161,13 +288,31 @@ def run():
|
||||
if 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)
|
||||
|
||||
# Lighten
|
||||
if args.scale:
|
||||
preset = ColorProfile([c.lighten(args.scale) for c in preset.colors])
|
||||
preset = preset.lighten(args.scale)
|
||||
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_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
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from subprocess import check_output
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
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 .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:
|
||||
@@ -21,26 +130,30 @@ def get_command_path() -> str:
|
||||
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 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):
|
||||
# Get existing ascii
|
||||
def run_neofetch(preset: ColorProfile, alignment: ColorAlignment):
|
||||
asc = get_distro_ascii()
|
||||
|
||||
# Remove existing colors
|
||||
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)])
|
||||
w, h = ascii_size(asc)
|
||||
asc = alignment.recolor_ascii(asc, preset)
|
||||
|
||||
# Write temp file
|
||||
with TemporaryDirectory() as tmp_dir:
|
||||
@@ -49,6 +162,29 @@ def run_neofetch(preset: ColorProfile, mode: AnsiMode):
|
||||
path.write_text(asc)
|
||||
|
||||
# Call neofetch with the temp file
|
||||
os.environ['ascii_len'] = str(max(len(l) for l in lines))
|
||||
os.environ['ascii_lines'] = str(len(lines))
|
||||
os.system(get_command_path() + f' --ascii --source {path.absolute()} --ascii-colors')
|
||||
os.environ['ascii_len'] = str(w)
|
||||
os.environ['ascii_lines'] = str(h)
|
||||
|
||||
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'),
|
||||
}
|
||||
|
||||
|
||||
+91
-7
@@ -1,8 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable
|
||||
|
||||
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:
|
||||
@@ -17,7 +29,6 @@ class ColorProfile:
|
||||
else:
|
||||
self.colors = colors
|
||||
|
||||
|
||||
def with_weights(self, weights: list[int]) -> list[RGB]:
|
||||
"""
|
||||
Map colors based on weights
|
||||
@@ -76,11 +87,59 @@ class ColorProfile:
|
||||
result += '\033[0m'
|
||||
result += t
|
||||
else:
|
||||
result += colors[i].to_ansi_rgb(foreground) + t
|
||||
result += colors[i].to_ansi(foreground=foreground) + t
|
||||
|
||||
result += '\033[0m'
|
||||
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] = {
|
||||
'rainbow': ColorProfile([
|
||||
'#E50000',
|
||||
@@ -98,12 +157,14 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#F6AAB7',
|
||||
'#55CDFD'
|
||||
]),
|
||||
|
||||
'nonbinary': ColorProfile([
|
||||
'#FCF431',
|
||||
'#FCFCFC',
|
||||
'#9D59D2',
|
||||
'#282828'
|
||||
]),
|
||||
|
||||
'agender': ColorProfile([
|
||||
'#000000',
|
||||
'#BABABA',
|
||||
@@ -113,11 +174,13 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#BABABA',
|
||||
'#000000'
|
||||
]),
|
||||
|
||||
'queer': ColorProfile([
|
||||
'#B57FDD',
|
||||
'#FFFFFF',
|
||||
'#49821E'
|
||||
]),
|
||||
|
||||
'genderfluid': ColorProfile([
|
||||
'#FE76A2',
|
||||
'#FFFFFF',
|
||||
@@ -131,11 +194,13 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#9B4F96',
|
||||
'#0038A8'
|
||||
]),
|
||||
|
||||
'pansexual': ColorProfile([
|
||||
'#FF1C8D',
|
||||
'#FFD700',
|
||||
'#1AB3FF'
|
||||
]),
|
||||
|
||||
'lesbian': ColorProfile([
|
||||
'#D62800',
|
||||
'#FF9B56',
|
||||
@@ -143,12 +208,14 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#D462A6',
|
||||
'#A40062'
|
||||
]),
|
||||
|
||||
'asexual': ColorProfile([
|
||||
'#000000',
|
||||
'#A4A4A4',
|
||||
'#FFFFFF',
|
||||
'#810081'
|
||||
]),
|
||||
|
||||
'aromantic': ColorProfile([
|
||||
'#3BA740',
|
||||
'#A8D47A',
|
||||
@@ -156,6 +223,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#ABABAB',
|
||||
'#000000'
|
||||
]),
|
||||
|
||||
# below sourced from https://www.flagcolorcodes.com/flags/pride
|
||||
# goto f"https://www.flagcolorcodes.com/{preset}" for info
|
||||
# todo: sane sorting
|
||||
@@ -163,6 +231,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#99D9EA',
|
||||
'#7F7F7F'
|
||||
]),
|
||||
|
||||
'intergender': ColorProfile([
|
||||
# todo: use weighted spacing
|
||||
'#900DC2',
|
||||
@@ -171,6 +240,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#900DC2',
|
||||
'#900DC2',
|
||||
]),
|
||||
|
||||
'greygender': ColorProfile([
|
||||
'#B3B3B3',
|
||||
'#B3B3B3',
|
||||
@@ -181,6 +251,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#535353',
|
||||
'#535353',
|
||||
]),
|
||||
|
||||
'akiosexual': ColorProfile([
|
||||
'#F9485E',
|
||||
'#FEA06A',
|
||||
@@ -188,6 +259,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#FFFFFF',
|
||||
'#000000',
|
||||
]),
|
||||
|
||||
'transmasculine': ColorProfile([
|
||||
'#FF8ABD',
|
||||
'#CDF5FE',
|
||||
@@ -197,6 +269,7 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#CDF5FE',
|
||||
'#FF8ABD',
|
||||
]),
|
||||
|
||||
'demifaun': ColorProfile([
|
||||
'#7F7F7F',
|
||||
'#7F7F7F',
|
||||
@@ -212,25 +285,29 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#7F7F7F',
|
||||
'#7F7F7F',
|
||||
]),
|
||||
|
||||
'neutrois': ColorProfile([
|
||||
'#FFFFFF',
|
||||
'#1F9F00',
|
||||
'#000000'
|
||||
]),
|
||||
'biromantic alt 2': ColorProfile([
|
||||
|
||||
'biromantic1': ColorProfile([
|
||||
'#8869A5',
|
||||
'#D8A7D8',
|
||||
'#FFFFFF',
|
||||
'#FDB18D',
|
||||
'#151638',
|
||||
]),
|
||||
'biromantic alt 2': ColorProfile([
|
||||
|
||||
'biromantic2': ColorProfile([
|
||||
'#740194',
|
||||
'#AEB1AA',
|
||||
'#FFFFFF',
|
||||
'#AEB1AA',
|
||||
'#740194',
|
||||
]),
|
||||
|
||||
'autoromantic': ColorProfile([ # symbol interpreted
|
||||
'#99D9EA',
|
||||
'#99D9EA',
|
||||
@@ -247,8 +324,9 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#7F7F7F',
|
||||
'#7F7F7F',
|
||||
]),
|
||||
|
||||
# i didn't expect this one to work. cool!
|
||||
'boyflux alt 2': ColorProfile([
|
||||
'boyflux2': ColorProfile([
|
||||
'#E48AE4',
|
||||
'#9A81B4',
|
||||
'#55BFAB',
|
||||
@@ -281,5 +359,11 @@ PRESETS: dict[str, ColorProfile] = {
|
||||
'#5276D4',
|
||||
]),
|
||||
|
||||
|
||||
'beiyang': ColorProfile([
|
||||
'#DF1B12',
|
||||
'#FFC600',
|
||||
'#01639D',
|
||||
'#FFFFFF',
|
||||
'#000000',
|
||||
]),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import inspect
|
||||
import json
|
||||
from datetime import datetime, date
|
||||
|
||||
@@ -41,3 +42,7 @@ def json_stringify(obj: object, indent: int | None = None) -> str:
|
||||
: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))
|
||||
|
||||
# Count chocolatey packages.
|
||||
[[ -d /cygdrive/c/ProgramData/chocolatey/lib ]] && \
|
||||
dir /cygdrive/c/ProgramData/chocolatey/lib/*
|
||||
# [[ -d /c/ProgramData/chocolatey/lib ]] && \
|
||||
# dir /c/ProgramData/chocolatey/lib/*
|
||||
has choco && tot choco list --localonly
|
||||
|
||||
# Count winget
|
||||
has winget && tot winget list
|
||||
;;
|
||||
|
||||
Haiku)
|
||||
@@ -11594,6 +11598,11 @@ main() {
|
||||
return 0
|
||||
}
|
||||
|
||||
get_ascii_distro_name() {
|
||||
get_distro
|
||||
echo "$ascii_distro"
|
||||
}
|
||||
|
||||
get_print_ascii() {
|
||||
cache_uname
|
||||
get_os
|
||||
@@ -11603,4 +11612,24 @@ get_print_ascii() {
|
||||
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 "$@"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import hyfetch
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -32,7 +32,7 @@ setup(
|
||||
packages=['hyfetch'],
|
||||
package_data={'hyfetch': ['hyfetch/*']},
|
||||
include_package_data=True,
|
||||
install_requires=['setuptools', 'typing_extensions'],
|
||||
install_requires=['setuptools', 'typing_extensions', 'numpy'],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"hyfetch=hyfetch.main:run",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from hyfetch.color_scale import test_color_scale
|
||||
from hyfetch.color_util import RGB, printc
|
||||
from hyfetch.neofetch_util import get_command_path, run_neofetch
|
||||
from hyfetch.presets import PRESETS
|
||||
@@ -35,3 +36,4 @@ def test_rgb_8bit_conversion():
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_rgb_8bit_conversion()
|
||||
test_color_scale()
|
||||
|
||||
Reference in New Issue
Block a user