diff --git a/hyfetch/color_util.py b/hyfetch/color_util.py index 45081f8e..a5b60fcd 100644 --- a/hyfetch/color_util.py +++ b/hyfetch/color_util.py @@ -1,13 +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", @@ -176,16 +177,26 @@ class RGB(NamedTuple): """ return RGB(*redistribute_rgb(*[v * multiplier for v in self])) - def set_light(self, light: float) -> '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 (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)]) - 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)]) + # 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)]) diff --git a/hyfetch/constants.py b/hyfetch/constants.py index e4e2e629..b2e6ef3f 100644 --- a/hyfetch/constants.py +++ b/hyfetch/constants.py @@ -4,6 +4,8 @@ import os from dataclasses import dataclass from pathlib import Path +from typing_extensions import Literal + CONFIG_PATH = Path.home() / '.config/hyfetch.json' VERSION = '1.0.7' @@ -37,5 +39,8 @@ class GlobalConfig: debug: bool is_light: bool + def light_dark(self) -> Literal['light', 'dark']: + return 'light' if self.is_light else 'dark' + 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 39044a37..98e37389 100755 --- a/hyfetch/main.py +++ b/hyfetch/main.py @@ -10,7 +10,7 @@ from typing import Iterable from hyfetch import presets -from .color_util import printc, color, clear_screen +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 @@ -98,7 +98,16 @@ def create_config() -> Config: title += f'\n&e1. Selected color mode: &r{color_system}' ############################## - # 2. Choose preset + # 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&e3. Light/Dark: &r{light_dark}' + + ############################## + # 3. Choose preset clear_screen(title) printc('&a2. Let\'s choose a flag!') printc('Available flag presets:') @@ -122,19 +131,10 @@ def create_config() -> Config: print() print() - tmp = PRESETS['rainbow'].set_light(.7).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', show_ops=False) _prs = PRESETS[preset] - title += f'\n&e2. Selected flag: &r{_prs.color_text(preset)}' - - ############################## - # 3. Select light/dark mode - clear_screen(title) - 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}' + title += f'\n&e3. Selected flag: &r{_prs.color_text(preset)}' ############################# # 4. Dim/lighten colors @@ -148,7 +148,7 @@ def create_config() -> Config: ratios = [col / (num_cols - 1) for col in range(num_cols)] ratios = [r * 0.6 + 0.2 for r in ratios] lines = [ColorAlignment('horizontal').recolor_ascii(TEST_ASCII.replace( - '{txt}', f'{r * 100:.0f}%'.center(5)), _prs.set_light(r)).split('\n') for r in ratios] + '{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: @@ -170,11 +170,12 @@ def create_config() -> Config: 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(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!') diff --git a/hyfetch/models.py b/hyfetch/models.py index faef1318..b36fccfe 100644 --- a/hyfetch/models.py +++ b/hyfetch/models.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from typing_extensions import Literal -from .color_util import AnsiMode +from .color_util import AnsiMode, LightDark from .constants import CONFIG_PATH from .neofetch_util import ColorAlignment from .serializer import json_stringify, from_dict @@ -14,7 +14,7 @@ from .serializer import json_stringify, from_dict class Config: preset: str mode: AnsiMode - light_dark: Literal['light', 'dark'] = 'dark' + light_dark: LightDark = 'dark' lightness: float | None = None color_align: ColorAlignment = ColorAlignment('horizontal') diff --git a/hyfetch/presets.py b/hyfetch/presets.py index c1bdc97b..01eb7d55 100644 --- a/hyfetch/presets.py +++ b/hyfetch/presets.py @@ -4,7 +4,8 @@ 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: @@ -100,17 +101,38 @@ class ColorProfile: """ return ColorProfile([c.lighten(multiplier) for c in self.colors]) - def set_light(self, light: float): + 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) for c in self.colors]) + return ColorProfile([c.set_light(light, at_least, at_most) 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 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 = GLOBAL_CFG.light_dark()): + """ + 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) + """ + light = 0.65 if term.lower() == 'dark' else 0.4 + return self.set_light_dl(light, term) def unique_colors(self) -> ColorProfile: """