hyfetch/hyfetch/presets.py
2022-10-30 18:50:41 +09:00

596 lines
14 KiB
Python

from __future__ import annotations
from typing import Iterable
from typing_extensions import Literal
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:
raw: list[str]
colors: list[RGB]
spacing: Literal['equal', 'weighted'] = 'equal'
def __init__(self, colors: list[str] | list[RGB]):
if isinstance(colors[0], str):
self.raw = colors
self.colors = [RGB.from_hex(c) for c in colors]
else:
self.colors = colors
def with_weights(self, weights: list[int]) -> list[RGB]:
"""
Map colors based on weights
:param weights: Weights of each color (weights[i] = how many times color[i] appears)
:return:
"""
return [c for i, w in enumerate(weights) for c in [self.colors[i]] * w]
def with_length(self, length: int) -> list[RGB]:
"""
Spread to a specific length of text
:param length: Length of text
:return: List of RGBs of the length
"""
preset_len = len(self.colors)
center_i = preset_len // 2
# How many copies of each color should be displayed at least?
repeats = length // preset_len
weights = [repeats] * preset_len
# How many extra space left?
extras = length % preset_len
# If there is an even space left, extend the center by one space
if extras % 2 == 1:
extras -= 1
weights[center_i] += 1
# Add weight to border until there's no space left (extras must be even at this point)
border_i = 0
while extras > 0:
extras -= 2
weights[border_i] += 1
weights[-(border_i + 1)] += 1
border_i += 1
return self.with_weights(weights)
def color_text(self, txt: str, foreground: bool = True, space_only: bool = False) -> str:
"""
Color a text
:param txt: Text
:param foreground: Whether the foreground text show the color or the background block
:param space_only: Whether to only color spaces
:return: Colored text
"""
colors = self.with_length(len(txt))
result = ''
for i, t in enumerate(txt):
if space_only and t != ' ':
if i > 0 and txt[i - 1] == ' ':
result += '\033[0m'
result += t
else:
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 | None = None):
"""
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)
"""
term = term or GLOBAL_CFG.light_dark()
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',
'#FF8D00',
'#FFEE00',
'#028121',
'#004CFF',
'#770088'
]),
'transgender': ColorProfile([
'#55CDFD',
'#F6AAB7',
'#FFFFFF',
'#F6AAB7',
'#55CDFD'
]),
'nonbinary': ColorProfile([
'#FCF431',
'#FCFCFC',
'#9D59D2',
'#282828'
]),
'agender': ColorProfile([
'#000000',
'#BABABA',
'#FFFFFF',
'#BAF484',
'#FFFFFF',
'#BABABA',
'#000000'
]),
'queer': ColorProfile([
'#B57FDD',
'#FFFFFF',
'#49821E'
]),
'genderfluid': ColorProfile([
'#FE76A2',
'#FFFFFF',
'#BF12D7',
'#000000',
'#303CBE'
]),
'bisexual': ColorProfile([
'#D60270',
'#9B4F96',
'#0038A8'
]),
'pansexual': ColorProfile([
'#FF1C8D',
'#FFD700',
'#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([
'#D62800',
'#FF9B56',
'#FFFFFF',
'#D462A6',
'#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([
'#000000',
'#A4A4A4',
'#FFFFFF',
'#810081'
]),
'aromantic': ColorProfile([
'#3BA740',
'#A8D47A',
'#FFFFFF',
'#ABABAB',
'#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',
]),
"finsexual": ColorProfile([
"#B18EDF",
"#D7B1E2",
"#F7CDE9",
"#F39FCE",
"#EA7BB3",
]),
'unlabeled1': ColorProfile([
'#EAF8E4',
'#FDFDFB',
'#E1EFF7',
'#F4E2C4'
]),
'unlabeled2': ColorProfile([
'#250548',
'#FFFFFF',
'#F7DCDA',
'#EC9BEE',
'#9541FA',
'#7D2557'
]),
'gendernonconforming1': ColorProfile(
ColorProfile([
'#50284d',
'#96467b',
'#5c96f7',
'#ffe6f7',
'#5c96f7',
'#96467b',
'#50284d'
]).with_weights([
4,1,1,1,1,1,4
])
),
'gendernonconforming2': ColorProfile([
'#50284d',
'#96467b',
'#5c96f7',
'#ffe6f7',
'#5c96f7',
'#96467b',
'#50284d'
]),
'femboy': ColorProfile([
"#d260a5",
"#e4afcd",
"#fefefe",
"#57cef8",
"#fefefe",
"#e4afcd",
"#d260a5"
]),
'tomboy': ColorProfile([
"#2f3fb9",
"#613a03",
"#fefefe",
"#f1a9b7",
"#fefefe",
"#613a03",
"#2f3fb9"
]),
}