httpie-cli/httpie/output/ui/palette.py
2022-05-05 11:17:12 -07:00

270 lines
7.1 KiB
Python

from dataclasses import dataclass, field
from enum import Enum, auto
from typing import Optional, List
PYGMENTS_BRIGHT_BLACK = 'ansibrightblack'
AUTO_STYLE = 'auto' # Follows terminal ANSI color styles
class Styles(Enum):
PIE = auto()
ANSI = auto()
class PieStyle(str, Enum):
UNIVERSAL = 'pie'
DARK = 'pie-dark'
LIGHT = 'pie-light'
PIE_STYLE_TO_SHADE = {
PieStyle.DARK: '500',
PieStyle.UNIVERSAL: '600',
PieStyle.LIGHT: '700',
}
SHADE_TO_PIE_STYLE = {
shade: style for style, shade in PIE_STYLE_TO_SHADE.items()
}
class ColorString(str):
def __or__(self, other: str) -> 'ColorString':
"""Combine a style with a property.
E.g: PieColor.BLUE | BOLD | ITALIC
"""
if isinstance(other, str):
# In case of PieColor.BLUE | SOMETHING
# we just create a new string.
return ColorString(self + ' ' + other)
elif isinstance(other, GenericColor):
# If we see a GenericColor, then we'll wrap it
# in with the desired property in a different class.
return _StyledGenericColor(other, styles=self.split())
elif isinstance(other, _StyledGenericColor):
# And if it is already wrapped, we'll just extend the
# list of properties.
other.styles.extend(self.split())
return other
else:
return NotImplemented
class PieColor(ColorString, Enum):
"""Styles that are available only in Pie themes."""
PRIMARY = 'primary'
SECONDARY = 'secondary'
WHITE = 'white'
BLACK = 'black'
GREY = 'grey'
AQUA = 'aqua'
PURPLE = 'purple'
ORANGE = 'orange'
RED = 'red'
BLUE = 'blue'
PINK = 'pink'
GREEN = 'green'
YELLOW = 'yellow'
class GenericColor(Enum):
"""Generic colors that are safe to use everywhere."""
# <https://rich.readthedocs.io/en/stable/appendix/colors.html>
WHITE = {Styles.PIE: PieColor.WHITE, Styles.ANSI: 'white'}
BLACK = {Styles.PIE: PieColor.BLACK, Styles.ANSI: 'black'}
GREEN = {Styles.PIE: PieColor.GREEN, Styles.ANSI: 'green'}
ORANGE = {Styles.PIE: PieColor.ORANGE, Styles.ANSI: 'yellow'}
YELLOW = {Styles.PIE: PieColor.YELLOW, Styles.ANSI: 'bright_yellow'}
BLUE = {Styles.PIE: PieColor.BLUE, Styles.ANSI: 'blue'}
PINK = {Styles.PIE: PieColor.PINK, Styles.ANSI: 'bright_magenta'}
PURPLE = {Styles.PIE: PieColor.PURPLE, Styles.ANSI: 'magenta'}
RED = {Styles.PIE: PieColor.RED, Styles.ANSI: 'red'}
AQUA = {Styles.PIE: PieColor.AQUA, Styles.ANSI: 'cyan'}
GREY = {Styles.PIE: PieColor.GREY, Styles.ANSI: 'bright_black'}
def apply_style(
self, style: Styles, *, style_name: Optional[str] = None
) -> str:
"""Apply the given style to a particular value."""
exposed_color = self.value[style]
if style is Styles.PIE:
assert style_name is not None
shade = PIE_STYLE_TO_SHADE[PieStyle(style_name)]
return get_color(exposed_color, shade)
else:
return exposed_color
@dataclass
class _StyledGenericColor:
color: 'GenericColor'
styles: List[str] = field(default_factory=list)
# noinspection PyDictCreation
COLOR_PALETTE = {
# Copy the brand palette
PieColor.WHITE: '#F5F5F0',
PieColor.BLACK: '#1C1818',
PieColor.GREY: {
'50': '#F5F5F0',
'100': '#EDEDEB',
'200': '#D1D1CF',
'300': '#B5B5B2',
'400': '#999999',
'500': '#7D7D7D',
'600': '#666663',
'700': '#4F4D4D',
'800': '#363636',
'900': '#1C1818',
'DEFAULT': '#7D7D7D',
},
PieColor.AQUA: {
'50': '#E8F0F5',
'100': '#D6E3ED',
'200': '#C4D9E5',
'300': '#B0CCDE',
'400': '#9EBFD6',
'500': '#8CB4CD',
'600': '#7A9EB5',
'700': '#698799',
'800': '#597082',
'900': '#455966',
'DEFAULT': '#8CB4CD',
},
PieColor.PURPLE: {
'50': '#F0E0FC',
'100': '#E3C7FA',
'200': '#D9ADF7',
'300': '#CC96F5',
'400': '#BF7DF2',
'500': '#B464F0',
'600': '#9E54D6',
'700': '#8745BA',
'800': '#70389E',
'900': '#5C2982',
'DEFAULT': '#B464F0',
},
PieColor.ORANGE: {
'50': '#FFEDDB',
'100': '#FFDEBF',
'200': '#FFCFA3',
'300': '#FFBF87',
'400': '#FFB06B',
'500': '#FFA24E',
'600': '#F2913D',
'700': '#E3822B',
'800': '#D6701C',
'900': '#C75E0A',
'DEFAULT': '#FFA24E',
},
PieColor.RED: {
'50': '#FFE0DE',
'100': '#FFC7C4',
'200': '#FFB0AB',
'300': '#FF968F',
'400': '#FF8075',
'500': '#FF665B',
'600': '#E34F45',
'700': '#C7382E',
'800': '#AD2117',
'900': '#910A00',
'DEFAULT': '#FF665B',
},
PieColor.BLUE: {
'50': '#DBE3FA',
'100': '#BFCFF5',
'200': '#A1B8F2',
'300': '#85A3ED',
'400': '#698FEB',
'500': '#4B78E6',
'600': '#426BD1',
'700': '#3B5EBA',
'800': '#3354A6',
'900': '#2B478F',
'DEFAULT': '#4B78E6',
},
PieColor.PINK: {
'50': '#FFEBFF',
'100': '#FCDBFC',
'200': '#FCCCFC',
'300': '#FCBAFC',
'400': '#FAABFA',
'500': '#FA9BFA',
'600': '#DE85DE',
'700': '#C26EC2',
'800': '#A854A6',
'900': '#8C3D8A',
'DEFAULT': '#FA9BFA',
},
PieColor.GREEN: {
'50': '#E3F7E8',
'100': '#CCF2D6',
'200': '#B5EDC4',
'300': '#A1E8B0',
'400': '#8AE09E',
'500': '#73DC8C',
'600': '#63C27A',
'700': '#52AB66',
'800': '#429154',
'900': '#307842',
'DEFAULT': '#73DC8C',
},
PieColor.YELLOW: {
'50': '#F7F7DB',
'100': '#F2F2BF',
'200': '#EDEDA6',
'300': '#E5E88A',
'400': '#E0E36E',
'500': '#DBDE52',
'600': '#CCCC3D',
'700': '#BABA29',
'800': '#ABA614',
'900': '#999400',
'DEFAULT': '#DBDE52',
},
}
COLOR_PALETTE.update(
{
# Terminal-specific palette customizations.
PieColor.GREY: {
# Grey is the same no matter shade for the colors
shade: COLOR_PALETTE[PieColor.GREY]['500']
for shade in COLOR_PALETTE[PieColor.GREY].keys()
},
PieColor.PRIMARY: {
'700': COLOR_PALETTE[PieColor.BLACK],
'600': PYGMENTS_BRIGHT_BLACK,
'500': COLOR_PALETTE[PieColor.WHITE],
},
PieColor.SECONDARY: {
'700': '#37523C',
'600': '#6c6969',
'500': '#6c6969',
},
}
)
def boldify(color: PieColor) -> str:
return f'bold {color}'
# noinspection PyDefaultArgument
def get_color(
color: PieColor, shade: str, *, palette=COLOR_PALETTE
) -> Optional[str]:
if color not in palette:
return None
color_code = palette[color]
if isinstance(color_code, dict) and shade in color_code:
return color_code[shade]
else:
return color_code