codekingpro/portable-devtools
114k
1from dataclasses import dataclass, field2from enum import Enum, auto3from typing import Optional, List4 5 6PYGMENTS_BRIGHT_BLACK = 'ansibrightblack'7 8AUTO_STYLE = 'auto' # Follows terminal ANSI color styles9 10 11class Styles(Enum):12 PIE = auto()13 ANSI = auto()14 15 16class PieStyle(str, Enum):17 UNIVERSAL = 'pie'18 DARK = 'pie-dark'19 LIGHT = 'pie-light'20 21 22PIE_STYLE_TO_SHADE = {23 PieStyle.DARK: '500',24 PieStyle.UNIVERSAL: '600',25 PieStyle.LIGHT: '700',26}27SHADE_TO_PIE_STYLE = {28 shade: style for style, shade in PIE_STYLE_TO_SHADE.items()29}30 31 32class ColorString(str):33 def __or__(self, other: str) -> 'ColorString':34 """Combine a style with a property.35 36 E.g: PieColor.BLUE | BOLD | ITALIC37 """38 if isinstance(other, str):39 # In case of PieColor.BLUE | SOMETHING40 # we just create a new string.41 return ColorString(self + ' ' + other)42 elif isinstance(other, GenericColor):43 # If we see a GenericColor, then we'll wrap it44 # in with the desired property in a different class.45 return _StyledGenericColor(other, styles=self.split())46 elif isinstance(other, _StyledGenericColor):47 # And if it is already wrapped, we'll just extend the48 # list of properties.49 other.styles.extend(self.split())50 return other51 else:52 return NotImplemented53 54 55class PieColor(ColorString, Enum):56 """Styles that are available only in Pie themes."""57 58 PRIMARY = 'primary'59 SECONDARY = 'secondary'60 61 WHITE = 'white'62 BLACK = 'black'63 GREY = 'grey'64 AQUA = 'aqua'65 PURPLE = 'purple'66 ORANGE = 'orange'67 RED = 'red'68 BLUE = 'blue'69 PINK = 'pink'70 GREEN = 'green'71 YELLOW = 'yellow'72 73 74class GenericColor(Enum):75 """Generic colors that are safe to use everywhere."""76 77 # <https://rich.readthedocs.io/en/stable/appendix/colors.html>78 79 WHITE = {Styles.PIE: PieColor.WHITE, Styles.ANSI: 'white'}80 BLACK = {Styles.PIE: PieColor.BLACK, Styles.ANSI: 'black'}81 GREEN = {Styles.PIE: PieColor.GREEN, Styles.ANSI: 'green'}82 ORANGE = {Styles.PIE: PieColor.ORANGE, Styles.ANSI: 'yellow'}83 YELLOW = {Styles.PIE: PieColor.YELLOW, Styles.ANSI: 'bright_yellow'}84 BLUE = {Styles.PIE: PieColor.BLUE, Styles.ANSI: 'blue'}85 PINK = {Styles.PIE: PieColor.PINK, Styles.ANSI: 'bright_magenta'}86 PURPLE = {Styles.PIE: PieColor.PURPLE, Styles.ANSI: 'magenta'}87 RED = {Styles.PIE: PieColor.RED, Styles.ANSI: 'red'}88 AQUA = {Styles.PIE: PieColor.AQUA, Styles.ANSI: 'cyan'}89 GREY = {Styles.PIE: PieColor.GREY, Styles.ANSI: 'bright_black'}90 91 def apply_style(92 self, style: Styles, *, style_name: Optional[str] = None93 ) -> str:94 """Apply the given style to a particular value."""95 exposed_color = self.value[style]96 if style is Styles.PIE:97 assert style_name is not None98 shade = PIE_STYLE_TO_SHADE[PieStyle(style_name)]99 return get_color(exposed_color, shade)100 else:101 return exposed_color102 103 104@dataclass105class _StyledGenericColor:106 color: 'GenericColor'107 styles: List[str] = field(default_factory=list)108 109 110# noinspection PyDictCreation111COLOR_PALETTE = {112 # Copy the brand palette113 PieColor.WHITE: '#F5F5F0',114 PieColor.BLACK: '#1C1818',115 PieColor.GREY: {116 '50': '#F5F5F0',117 '100': '#EDEDEB',118 '200': '#D1D1CF',119 '300': '#B5B5B2',120 '400': '#999999',121 '500': '#7D7D7D',122 '600': '#666663',123 '700': '#4F4D4D',124 '800': '#363636',125 '900': '#1C1818',126 'DEFAULT': '#7D7D7D',127 },128 PieColor.AQUA: {129 '50': '#E8F0F5',130 '100': '#D6E3ED',131 '200': '#C4D9E5',132 '300': '#B0CCDE',133 '400': '#9EBFD6',134 '500': '#8CB4CD',135 '600': '#7A9EB5',136 '700': '#698799',137 '800': '#597082',138 '900': '#455966',139 'DEFAULT': '#8CB4CD',140 },141 PieColor.PURPLE: {142 '50': '#F0E0FC',143 '100': '#E3C7FA',144 '200': '#D9ADF7',145 '300': '#CC96F5',146 '400': '#BF7DF2',147 '500': '#B464F0',148 '600': '#9E54D6',149 '700': '#8745BA',150 '800': '#70389E',151 '900': '#5C2982',152 'DEFAULT': '#B464F0',153 },154 PieColor.ORANGE: {155 '50': '#FFEDDB',156 '100': '#FFDEBF',157 '200': '#FFCFA3',158 '300': '#FFBF87',159 '400': '#FFB06B',160 '500': '#FFA24E',161 '600': '#F2913D',162 '700': '#E3822B',163 '800': '#D6701C',164 '900': '#C75E0A',165 'DEFAULT': '#FFA24E',166 },167 PieColor.RED: {168 '50': '#FFE0DE',169 '100': '#FFC7C4',170 '200': '#FFB0AB',171 '300': '#FF968F',172 '400': '#FF8075',173 '500': '#FF665B',174 '600': '#E34F45',175 '700': '#C7382E',176 '800': '#AD2117',177 '900': '#910A00',178 'DEFAULT': '#FF665B',179 },180 PieColor.BLUE: {181 '50': '#DBE3FA',182 '100': '#BFCFF5',183 '200': '#A1B8F2',184 '300': '#85A3ED',185 '400': '#698FEB',186 '500': '#4B78E6',187 '600': '#426BD1',188 '700': '#3B5EBA',189 '800': '#3354A6',190 '900': '#2B478F',191 'DEFAULT': '#4B78E6',192 },193 PieColor.PINK: {194 '50': '#FFEBFF',195 '100': '#FCDBFC',196 '200': '#FCCCFC',197 '300': '#FCBAFC',198 '400': '#FAABFA',199 '500': '#FA9BFA',200 '600': '#DE85DE',201 '700': '#C26EC2',202 '800': '#A854A6',203 '900': '#8C3D8A',204 'DEFAULT': '#FA9BFA',205 },206 PieColor.GREEN: {207 '50': '#E3F7E8',208 '100': '#CCF2D6',209 '200': '#B5EDC4',210 '300': '#A1E8B0',211 '400': '#8AE09E',212 '500': '#73DC8C',213 '600': '#63C27A',214 '700': '#52AB66',215 '800': '#429154',216 '900': '#307842',217 'DEFAULT': '#73DC8C',218 },219 PieColor.YELLOW: {220 '50': '#F7F7DB',221 '100': '#F2F2BF',222 '200': '#EDEDA6',223 '300': '#E5E88A',224 '400': '#E0E36E',225 '500': '#DBDE52',226 '600': '#CCCC3D',227 '700': '#BABA29',228 '800': '#ABA614',229 '900': '#999400',230 'DEFAULT': '#DBDE52',231 },232}233COLOR_PALETTE.update(234 {235 # Terminal-specific palette customizations.236 PieColor.GREY: {237 # Grey is the same no matter shade for the colors238 shade: COLOR_PALETTE[PieColor.GREY]['500']239 for shade in COLOR_PALETTE[PieColor.GREY].keys()240 },241 PieColor.PRIMARY: {242 '700': COLOR_PALETTE[PieColor.BLACK],243 '600': PYGMENTS_BRIGHT_BLACK,244 '500': COLOR_PALETTE[PieColor.WHITE],245 },246 PieColor.SECONDARY: {247 '700': '#37523C',248 '600': '#6c6969',249 '500': '#6c6969',250 },251 }252)253 254 255def boldify(color: PieColor) -> str:256 return f'bold {color}'257 258 259# noinspection PyDefaultArgument260def get_color(261 color: PieColor, shade: str, *, palette=COLOR_PALETTE262) -> Optional[str]:263 if color not in palette:264 return None265 color_code = palette[color]266 if isinstance(color_code, dict) and shade in color_code:267 return color_code[shade]268 else:269 return color_code270 