teddecor.TED.formatting

Colors

Colors must be RGB, Xterm index, predefined, or hex.

  • RGB = 255,255,255 ... [@F 255,255,255]
  • Xterm = 0-255 ... [@B 123]
  • hex = #aaa or #aaaaaa ... [@F #abc] or [@B #abcabc]
  • predefined = black, red, green, yellow, blue, magenta, cyan, and white ... [@F green]

Function macros can be of types rainbow, gradient, esc, and repr

  • rainbow takes a string and returns a rainbow formatted string
  • gradient is more complex. It takes a comma seperated string with the format {color n}...,string.
    • All but last value is the color value. Must have at least two colors which are hex.
    • Finally, the last value is the string that the gradient will be applied too.
  • esc takes a string and returns the literal value thus escaping the markup
  • repr takes a given string and returns the literal value surrounded by '
  1"""Colors
  2
  3Colors must be RGB, Xterm index, predefined, or hex.
  4
  5* RGB = 255,255,255 ... `[@F 255,255,255]`
  6* Xterm = 0-255 ... `[@B 123]`
  7* hex = #aaa or #aaaaaa ... `[@F #abc]` or `[@B #abcabc]`
  8* predefined = black, red, green, yellow, blue, magenta, cyan, and white ... `[@F green]`
  9
 10Function macros can be of types rainbow, gradient, esc, and repr
 11
 12* rainbow takes a string and returns a rainbow formatted string
 13* gradient is more complex. It takes a comma seperated string with the format {color n}...,string.
 14    * All but last value is the color value. Must have at least two colors which are hex.
 15    * Finally, the last value is the string that the gradient will be applied too.
 16* esc takes a string and returns the literal value thus escaping the markup
 17* repr takes a given string and returns the literal value surrounded by `'`
 18"""
 19from __future__ import annotations
 20from dataclasses import dataclass
 21from typing import Callable, Union
 22
 23__all__ = [
 24    "UNDERLINE",
 25    "BOLD",
 26    "RESET",
 27    "FUNC",
 28    "build_color",
 29]
 30
 31
 32@dataclass
 33class ColorType:
 34    FG: int = 30
 35    BG: int = 40
 36    BOTH: list = (30, 40)
 37
 38
 39FUNC = {
 40    "rainbow": lambda string: __RAINBOW(string),
 41    "repr": lambda string: repr(string),
 42}
 43
 44PREDEFINED = {
 45    "black": lambda c: f"{c + 0}",
 46    "red": lambda c: f"{c + 1}",
 47    "green": lambda c: f"{c + 2}",
 48    "yellow": lambda c: f"{c + 3}",
 49    "blue": lambda c: f"{c + 4}",
 50    "magenta": lambda c: f"{c + 5}",
 51    "cyan": lambda c: f"{c + 6}",
 52    "white": lambda c: f"{c + 7}",
 53}
 54RGB = lambda c, r, g, b: f"{c + 8};2;{r};{g};{b}"
 55XTERM = lambda c, v: f"{c + 8};5;{v}"
 56RESETCOLOR = lambda ctype: f"{ctype + 9}"
 57RESET = "\x1b[0m"
 58
 59
 60def HEX(context: int, hex: str) -> str:
 61    """Converts 3 or 6 digit hex into an rgb literal
 62
 63    Args:
 64        context (int): Whether the hex is for the foreground or background
 65        hex (str): The hex code
 66
 67    Raises:
 68        ValueError: If the hex code is not in the valid format
 69
 70    Returns:
 71        str: Ansi RGB color
 72    """
 73    hex = hex.lstrip("#")
 74    l = len(hex)
 75
 76    if l == 6:
 77        r, g, b = tuple(int(hex[i : i + 2], 16) for i in (0, 2, 4))
 78        return RGB(context, r, g, b)
 79    elif l == 3:
 80        hex = "".join(h + h for h in hex)
 81        r, g, b = tuple(int(hex[i : i + 2], 16) for i in (0, 2))
 82        return RGB(context, r, g, b)
 83
 84    raise ValueError(f"Expected hex with length of 3 or 6")
 85
 86
 87@dataclass
 88class BOLD:
 89    """The bold value based on the current toggle value."""
 90
 91    POP: int = 22
 92    PUSH: int = 1
 93
 94    @staticmethod
 95    def inverse(current: int) -> BOLD:
 96        return BOLD.POP if current == BOLD.PUSH else BOLD.PUSH
 97
 98
 99@dataclass
100class UNDERLINE:
101    """The bold value based on the current toggle value."""
102
103    POP: int = 24
104    PUSH: int = 4
105
106    @staticmethod
107    def inverse(current: int):
108        return UNDERLINE.POP if current == UNDERLINE.PUSH else UNDERLINE.PUSH
109
110
111@dataclass
112class LINK:
113    CLOSE: str = "\x1b]8;;\x1b\\"
114    OPEN: str = lambda url: f"\x1b]8;;{url}\x1b\\"
115
116
117def get_color(types: Union[int, list[int]], content: str) -> Union[int, list[int]]:
118    """Parse and translate the color value from hex, xterm, rgb, and predefined color values.
119
120    Args:
121        types (Union[int, list[int]]): Tells what types to generate the color for. This include fg, bg, and both fg and bg.
122        content (str): The color string that is to be parsed
123
124    Returns:
125        Union[int, list[int]]: List of color code values according to the needed types
126    """
127    from re import match
128
129    results = []
130    content = content.lower()
131    for ctype in types:
132        if len(content) == 0:
133            results.append(RESETCOLOR(ctype))
134        elif content.startswith("#"):
135            if len(content) == 4 and match(r"#[a-fA-F0-9]{3}", content):
136                results.append(HEX(ctype, content))
137            elif len(content) == 7 and match(r"#[a-fA-F0-9]{6}", content):
138                results.append(HEX(ctype, content))
139        elif match(r"\d{1,3}\s*[,;]\s*\d{1,3}\s*[,;]\s*\d{1,3}", content):
140            rgb = content.replace(";", ",").split(",")
141            results.append(RGB(ctype, rgb[0].strip(), rgb[1].strip(), rgb[2].strip()))
142        elif match(r"\d{1,3}", content):
143            results.append(XTERM(ctype, content))
144        else:
145            if content in PREDEFINED:
146                results.append(PREDEFINED[content](ctype))
147            else:
148                raise ValueError(
149                    f"The color, \x1b[1;31m{content}\x1b[0m, does not match any valid format"
150                )
151
152    return results
153
154
155def build_color(color: str) -> tuple[ColorType, str]:
156    """Takes a color macro and determines if it is type fg, bg, or both.
157    It will get the color string and produce color codes for each type that was specified.
158
159    Args:
160        color (str): The color macro to parse
161
162    Returns:
163        tuple[ColorType, str]: Tuple of the ColorType fg, bg, or both, along with the color codes
164    """
165    color = color[1:]
166    ctype = ColorType.BOTH
167    content = color.strip()
168
169    if color.startswith(("F", "B")):
170        ctype = color[0]
171        content = color[1:].strip(" ")
172        if ctype == "F":
173            ctype = [ColorType.FG]
174        elif ctype == "B":
175            ctype = [ColorType.BG]
176
177    return ctype, get_color(ctype, content)
178
179
180def __RAINBOW(input: str) -> str:
181    """Take a string input and make each character rainbow
182
183    Args:
184        input (str): The string to make into a rainbow
185
186    Returns:
187        str: Rainbow string
188    """
189    # red orange yellow green blue purple
190    context = 30
191    colors = [
192        f"\x1b[{XTERM(context, 196)}m",
193        f"\x1b[{XTERM(context, 202)}m",
194        f"\x1b[{XTERM(context, 190)}m",
195        f"\x1b[{XTERM(context, 41)}m",
196        f"\x1b[{XTERM(context, 39)}m",
197        f"\x1b[{XTERM(context, 92)}m",
198    ]
199
200    out = []
201    for i, char in enumerate(input):
202        out.append(colors[i % len(colors)] + char)
203    out.append("\x1b[39;49m")
204
205    return "".join(out)
@dataclass
class UNDERLINE:
100@dataclass
101class UNDERLINE:
102    """The bold value based on the current toggle value."""
103
104    POP: int = 24
105    PUSH: int = 4
106
107    @staticmethod
108    def inverse(current: int):
109        return UNDERLINE.POP if current == UNDERLINE.PUSH else UNDERLINE.PUSH

The bold value based on the current toggle value.

UNDERLINE(POP: int = 24, PUSH: int = 4)
POP: int = 24
PUSH: int = 4
@staticmethod
def inverse(current: int)
107    @staticmethod
108    def inverse(current: int):
109        return UNDERLINE.POP if current == UNDERLINE.PUSH else UNDERLINE.PUSH
@dataclass
class BOLD:
88@dataclass
89class BOLD:
90    """The bold value based on the current toggle value."""
91
92    POP: int = 22
93    PUSH: int = 1
94
95    @staticmethod
96    def inverse(current: int) -> BOLD:
97        return BOLD.POP if current == BOLD.PUSH else BOLD.PUSH

The bold value based on the current toggle value.

BOLD(POP: int = 22, PUSH: int = 1)
POP: int = 22
PUSH: int = 1
@staticmethod
def inverse(current: int) -> teddecor.TED.formatting.BOLD:
95    @staticmethod
96    def inverse(current: int) -> BOLD:
97        return BOLD.POP if current == BOLD.PUSH else BOLD.PUSH
RESET = '\x1b[0m'
FUNC = {'rainbow': <function <lambda>>, 'repr': <function <lambda>>}
def build_color(color: str) -> tuple[teddecor.TED.formatting.ColorType, str]:
156def build_color(color: str) -> tuple[ColorType, str]:
157    """Takes a color macro and determines if it is type fg, bg, or both.
158    It will get the color string and produce color codes for each type that was specified.
159
160    Args:
161        color (str): The color macro to parse
162
163    Returns:
164        tuple[ColorType, str]: Tuple of the ColorType fg, bg, or both, along with the color codes
165    """
166    color = color[1:]
167    ctype = ColorType.BOTH
168    content = color.strip()
169
170    if color.startswith(("F", "B")):
171        ctype = color[0]
172        content = color[1:].strip(" ")
173        if ctype == "F":
174            ctype = [ColorType.FG]
175        elif ctype == "B":
176            ctype = [ColorType.BG]
177
178    return ctype, get_color(ctype, content)

Takes a color macro and determines if it is type fg, bg, or both. It will get the color string and produce color codes for each type that was specified.

Args: color (str): The color macro to parse

Returns: tuple[ColorType, str]: Tuple of the ColorType fg, bg, or both, along with the color codes