diff --git a/hyfetch/__init__.py b/hyfetch/__init__.py index 26e65ae2..4d44c4e4 100644 --- a/hyfetch/__init__.py +++ b/hyfetch/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations from . import main - __version__ = main.VERSION diff --git a/hyfetch/color_scale.py b/hyfetch/color_scale.py index 34ec0194..e5b3616f 100644 --- a/hyfetch/color_scale.py +++ b/hyfetch/color_scale.py @@ -1,9 +1,10 @@ from __future__ import annotations import numpy as np -from .color_util import RGB from numpy import ndarray +from .color_util import RGB + def create_gradient_hex(colors: list[str], resolution: int = 300) -> ndarray: """ diff --git a/hyfetch/color_util.py b/hyfetch/color_util.py index 855b7ee6..45081f8e 100644 --- a/hyfetch/color_util.py +++ b/hyfetch/color_util.py @@ -2,6 +2,7 @@ from __future__ import annotations import colorsys from typing import NamedTuple + from typing_extensions import Literal from .constants import GLOBAL_CFG @@ -184,3 +185,7 @@ class RGB(NamedTuple): """ 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)]) + + def set_min_light(self, light: float) -> 'RGB': + 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, max(l, light), s)]) diff --git a/hyfetch/constants.py b/hyfetch/constants.py index f904c86a..e4e2e629 100644 --- a/hyfetch/constants.py +++ b/hyfetch/constants.py @@ -35,6 +35,7 @@ class GlobalConfig: color_mode: str override_distro: str | None debug: bool + is_light: bool -GLOBAL_CFG = GlobalConfig(color_mode='8bit', override_distro=None, debug=False) +GLOBAL_CFG = GlobalConfig(color_mode='8bit', override_distro=None, debug=False, is_light=False) diff --git a/hyfetch/main.py b/hyfetch/main.py index 8de4f9f3..d60ac279 100755 --- a/hyfetch/main.py +++ b/hyfetch/main.py @@ -5,30 +5,16 @@ import argparse import json import random import re -from dataclasses import dataclass from itertools import permutations from typing import Iterable -from typing_extensions import Literal +from hyfetch import presets -from .color_util import AnsiMode, printc, color, clear_screen +from .color_util import printc, color, clear_screen from .constants import CONFIG_PATH, VERSION, TERM_LEN, TEST_ASCII_WIDTH, TEST_ASCII, GLOBAL_CFG -from .neofetch_util import run_neofetch, get_distro_ascii, ColorAlignment, ascii_size +from .models import Config +from .neofetch_util import run_neofetch, get_distro_ascii, ColorAlignment, ascii_size, color_alignments from .presets import PRESETS -from .serializer import json_stringify - - -@dataclass -class Config: - preset: str - mode: AnsiMode - light_dark: Literal['light', 'dark'] = 'dark' - lightness: float | None = None - color_align: ColorAlignment = ColorAlignment('horizontal') - - def save(self): - CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True) - CONFIG_PATH.write_text(json_stringify(self), 'utf-8') def check_config() -> Config: @@ -147,6 +133,7 @@ def create_config() -> Config: light_dark = literal_input(f'3. 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&e3. Light/Dark: &r{light_dark}' ############################# @@ -265,7 +252,9 @@ def run(): 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('--test-distro', help=color(f'Test print a specific distro\'s ascii art')) + 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() @@ -281,6 +270,10 @@ def run(): if args.debug: GLOBAL_CFG.debug = True + if args.test_print: + print(get_distro_ascii()) + return + # Load config config = check_config() @@ -296,6 +289,7 @@ def run(): # 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) @@ -308,5 +302,16 @@ def run(): if config.lightness: preset = preset.set_light(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.color_align) diff --git a/hyfetch/models.py b/hyfetch/models.py new file mode 100644 index 00000000..2ad68835 --- /dev/null +++ b/hyfetch/models.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from typing_extensions import Literal + +from .color_util import AnsiMode +from .constants import CONFIG_PATH +from .neofetch_util import ColorAlignment +from .serializer import json_stringify + + +@dataclass +class Config: + preset: str + mode: AnsiMode + light_dark: Literal['light', 'dark'] = 'dark' + lightness: float | None = None + color_align: ColorAlignment = ColorAlignment('horizontal') + + def save(self): + CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True) + CONFIG_PATH.write_text(json_stringify(self), 'utf-8') diff --git a/hyfetch/neofetch_util.py b/hyfetch/neofetch_util.py index ca8e9cde..a6b80f8b 100644 --- a/hyfetch/neofetch_util.py +++ b/hyfetch/neofetch_util.py @@ -10,13 +10,12 @@ from subprocess import check_output from tempfile import TemporaryDirectory import pkg_resources -from hyfetch.color_util import color from typing_extensions import Literal +from hyfetch.color_util import color from .constants import GLOBAL_CFG from .presets import ColorProfile - RE_NEOFETCH_COLOR = re.compile('\\${c[0-9]}') @@ -38,6 +37,26 @@ def normalize_ascii(asc: str) -> str: 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'] @@ -45,13 +64,35 @@ class ColorAlignment: # 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] = () + def recolor_ascii(self, asc: str, preset: ColorProfile) -> str: """ Use the color alignment to recolor an ascii art :return Colored ascii, Uncolored lines """ - if self.mode in ['horizontal', 'vertical']: + 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') @@ -67,28 +108,9 @@ class ColorAlignment: preset = preset.unique_colors() # Apply colors - new = [] - start_color = None color_map = {ai: preset.colors[pi].to_ansi() for ai, pi in self.custom_colors.items()} - for line in asc.split('\n'): - # Line has color placeholders - if len(RE_NEOFETCH_COLOR.findall(line)) > 0: - # Get the last placeholder for the next line - last = int(RE_NEOFETCH_COLOR.findall(line)[-1][3]) - - # Replace placeholders - for ascii_i, c in color_map.items(): - line = line.replace(f'${{c{ascii_i}}}', c) - - # Add to new ascii - new.append(f'{start_color or ""}{line}') - - # Change next start color - start_color = color_map[last] - else: - new.append(f'{start_color or ""}{line}') - - asc = '\n'.join(new) + for ascii_i, c in color_map.items(): + asc = asc.replace(f'${{c{ascii_i}}}', c) return asc @@ -111,8 +133,9 @@ def get_distro_ascii(distro: str | None = None) -> str: """ if not distro and GLOBAL_CFG.override_distro: distro = GLOBAL_CFG.override_distro - print(distro) - print(GLOBAL_CFG) + if GLOBAL_CFG.debug: + print(distro) + print(GLOBAL_CFG) cmd = 'print_ascii' if distro: os.environ['CUSTOM_DISTRO'] = distro @@ -148,3 +171,14 @@ def run_neofetch(preset: ColorProfile, alignment: ColorAlignment): # 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'), +} + diff --git a/hyfetch/presets.py b/hyfetch/presets.py index 1a12d12d..c1bdc97b 100644 --- a/hyfetch/presets.py +++ b/hyfetch/presets.py @@ -109,6 +109,9 @@ class ColorProfile: """ return ColorProfile([c.set_light(light) for c in self.colors]) + def set_min_light(self, light: float): + return ColorProfile([c.set_min_light(light) for c in self.colors]) + def unique_colors(self) -> ColorProfile: """ Create another color profile with only the unique colors diff --git a/neofetch b/neofetch index 4dfb266d..576a35ff 100755 --- a/neofetch +++ b/neofetch @@ -11614,4 +11614,17 @@ get_print_custom_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 "$@"