[+] Recommended color alignments
This commit is contained in:
parent
59f5fd5651
commit
d11796ef02
9 changed files with 132 additions and 48 deletions
|
@ -2,7 +2,6 @@ from __future__ import annotations
|
||||||
|
|
||||||
from . import main
|
from . import main
|
||||||
|
|
||||||
|
|
||||||
__version__ = main.VERSION
|
__version__ = main.VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from .color_util import RGB
|
|
||||||
from numpy import ndarray
|
from numpy import ndarray
|
||||||
|
|
||||||
|
from .color_util import RGB
|
||||||
|
|
||||||
|
|
||||||
def create_gradient_hex(colors: list[str], resolution: int = 300) -> ndarray:
|
def create_gradient_hex(colors: list[str], resolution: int = 300) -> ndarray:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import colorsys
|
import colorsys
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from .constants import GLOBAL_CFG
|
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])
|
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)])
|
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)])
|
||||||
|
|
|
@ -35,6 +35,7 @@ class GlobalConfig:
|
||||||
color_mode: str
|
color_mode: str
|
||||||
override_distro: str | None
|
override_distro: str | None
|
||||||
debug: bool
|
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)
|
||||||
|
|
|
@ -5,30 +5,16 @@ import argparse
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
|
||||||
from itertools import permutations
|
from itertools import permutations
|
||||||
from typing import Iterable
|
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 .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 .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:
|
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 = literal_input(f'3. Is your terminal in &gf(#85e7e9)light mode&r or &gf(#c471ed)dark mode&r?',
|
||||||
['light', 'dark'], 'dark')
|
['light', 'dark'], 'dark')
|
||||||
is_light = light_dark == 'light'
|
is_light = light_dark == 'light'
|
||||||
|
GLOBAL_CFG.is_light = is_light
|
||||||
title += f'\n&e3. Light/Dark: &r{light_dark}'
|
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('--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('-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', 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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -281,6 +270,10 @@ def run():
|
||||||
if args.debug:
|
if args.debug:
|
||||||
GLOBAL_CFG.debug = True
|
GLOBAL_CFG.debug = True
|
||||||
|
|
||||||
|
if args.test_print:
|
||||||
|
print(get_distro_ascii())
|
||||||
|
return
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
config = check_config()
|
config = check_config()
|
||||||
|
|
||||||
|
@ -296,6 +289,7 @@ def run():
|
||||||
|
|
||||||
# Override global color mode
|
# Override global color mode
|
||||||
GLOBAL_CFG.color_mode = config.mode
|
GLOBAL_CFG.color_mode = config.mode
|
||||||
|
GLOBAL_CFG.is_light = config.light_dark == 'light'
|
||||||
|
|
||||||
# Get preset
|
# Get preset
|
||||||
preset = PRESETS.get(config.preset)
|
preset = PRESETS.get(config.preset)
|
||||||
|
@ -308,5 +302,16 @@ def run():
|
||||||
if config.lightness:
|
if config.lightness:
|
||||||
preset = preset.set_light(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
|
||||||
run_neofetch(preset, config.color_align)
|
run_neofetch(preset, config.color_align)
|
||||||
|
|
23
hyfetch/models.py
Normal file
23
hyfetch/models.py
Normal file
|
@ -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')
|
|
@ -10,13 +10,12 @@ from subprocess import check_output
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from hyfetch.color_util import color
|
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
from hyfetch.color_util import color
|
||||||
from .constants import GLOBAL_CFG
|
from .constants import GLOBAL_CFG
|
||||||
from .presets import ColorProfile
|
from .presets import ColorProfile
|
||||||
|
|
||||||
|
|
||||||
RE_NEOFETCH_COLOR = re.compile('\\${c[0-9]}')
|
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'))
|
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
|
@dataclass
|
||||||
class ColorAlignment:
|
class ColorAlignment:
|
||||||
mode: Literal['horizontal', 'vertical', 'custom']
|
mode: Literal['horizontal', 'vertical', 'custom']
|
||||||
|
@ -45,13 +64,35 @@ class ColorAlignment:
|
||||||
# custom_colors[ascii color index] = unique color index in preset
|
# custom_colors[ascii color index] = unique color index in preset
|
||||||
custom_colors: dict[int, int] = ()
|
custom_colors: dict[int, int] = ()
|
||||||
|
|
||||||
|
# Foreground/background ascii color index
|
||||||
|
fore_back: tuple[int, int] = ()
|
||||||
|
|
||||||
def recolor_ascii(self, asc: str, preset: ColorProfile) -> str:
|
def recolor_ascii(self, asc: str, preset: ColorProfile) -> str:
|
||||||
"""
|
"""
|
||||||
Use the color alignment to recolor an ascii art
|
Use the color alignment to recolor an ascii art
|
||||||
|
|
||||||
:return Colored ascii, Uncolored lines
|
: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
|
# Remove existing colors
|
||||||
asc = re.sub(RE_NEOFETCH_COLOR, '', asc)
|
asc = re.sub(RE_NEOFETCH_COLOR, '', asc)
|
||||||
lines = asc.split('\n')
|
lines = asc.split('\n')
|
||||||
|
@ -67,28 +108,9 @@ class ColorAlignment:
|
||||||
preset = preset.unique_colors()
|
preset = preset.unique_colors()
|
||||||
|
|
||||||
# Apply colors
|
# Apply colors
|
||||||
new = []
|
|
||||||
start_color = None
|
|
||||||
color_map = {ai: preset.colors[pi].to_ansi() for ai, pi in self.custom_colors.items()}
|
color_map = {ai: preset.colors[pi].to_ansi() for ai, pi in self.custom_colors.items()}
|
||||||
for line in asc.split('\n'):
|
for ascii_i, c in color_map.items():
|
||||||
# Line has color placeholders
|
asc = asc.replace(f'${{c{ascii_i}}}', c)
|
||||||
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)
|
|
||||||
|
|
||||||
return asc
|
return asc
|
||||||
|
|
||||||
|
@ -111,8 +133,9 @@ def get_distro_ascii(distro: str | None = None) -> str:
|
||||||
"""
|
"""
|
||||||
if not distro and GLOBAL_CFG.override_distro:
|
if not distro and GLOBAL_CFG.override_distro:
|
||||||
distro = GLOBAL_CFG.override_distro
|
distro = GLOBAL_CFG.override_distro
|
||||||
print(distro)
|
if GLOBAL_CFG.debug:
|
||||||
print(GLOBAL_CFG)
|
print(distro)
|
||||||
|
print(GLOBAL_CFG)
|
||||||
cmd = 'print_ascii'
|
cmd = 'print_ascii'
|
||||||
if distro:
|
if distro:
|
||||||
os.environ['CUSTOM_DISTRO'] = distro
|
os.environ['CUSTOM_DISTRO'] = distro
|
||||||
|
@ -148,3 +171,14 @@ def run_neofetch(preset: ColorProfile, alignment: ColorAlignment):
|
||||||
# print(full_cmd)
|
# print(full_cmd)
|
||||||
|
|
||||||
subprocess.run(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'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,9 @@ class ColorProfile:
|
||||||
"""
|
"""
|
||||||
return ColorProfile([c.set_light(light) for c in self.colors])
|
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:
|
def unique_colors(self) -> ColorProfile:
|
||||||
"""
|
"""
|
||||||
Create another color profile with only the unique colors
|
Create another color profile with only the unique colors
|
||||||
|
|
13
neofetch
13
neofetch
|
@ -11614,4 +11614,17 @@ get_print_custom_ascii() {
|
||||||
echo "$ascii_data"
|
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 "$@"
|
||||||
|
|
Loading…
Reference in a new issue