xulbux 1.7.0__py3-none-any.whl → 1.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xulbux might be problematic. Click here for more details.

xulbux/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.7.0"
1
+ __version__ = "1.7.1"
2
2
  __author__ = "XulbuX"
3
3
  __email__ = "xulbux.real@gmail.com"
4
4
  __license__ = "MIT"
xulbux/xx_color.py CHANGED
@@ -33,7 +33,7 @@ The `Color` class, which contains all sorts of different color-related methods:
33
33
 
34
34
  from .xx_regex import Regex
35
35
 
36
- from typing import Annotated, TypeAlias, Iterator, Optional, Literal, Union
36
+ from typing import Annotated, TypeAlias, Iterator, Optional, Literal, Union, Any, cast
37
37
  import re as _re
38
38
 
39
39
 
@@ -42,6 +42,10 @@ Int_0_255 = Annotated[int, "An integer value between 0 and 255, inclusive."]
42
42
  Int_0_360 = Annotated[int, "An integer value between 0 and 360, inclusive."]
43
43
  Float_0_1 = Annotated[float, "A float value between 0.0 and 1.0, inclusive."]
44
44
 
45
+ AnyRgba: TypeAlias = Any
46
+ AnyHsla: TypeAlias = Any
47
+ AnyHexa: TypeAlias = Any
48
+
45
49
  Rgba: TypeAlias = Union[
46
50
  tuple[Int_0_255, Int_0_255, Int_0_255],
47
51
  tuple[Int_0_255, Int_0_255, Int_0_255, Float_0_1],
@@ -178,7 +182,7 @@ class rgba:
178
182
  self.a = 1 - self.a
179
183
  return rgba(self.r, self.g, self.b, self.a, _validate=False)
180
184
 
181
- def grayscale(self, method: str = "wcag2") -> "rgba":
185
+ def grayscale(self, method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2") -> "rgba":
182
186
  """Converts the color to grayscale using the luminance formula.\n
183
187
  ------------------------------------------------------------------
184
188
  The `method` is the luminance calculation method to use:
@@ -387,7 +391,7 @@ class hsla:
387
391
  self.a = 1 - self.a
388
392
  return hsla(self.h, self.s, self.l, self.a, _validate=False)
389
393
 
390
- def grayscale(self, method: str = "wcag2") -> "hsla":
394
+ def grayscale(self, method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2") -> "hsla":
391
395
  """Converts the color to grayscale using the luminance formula.\n
392
396
  ------------------------------------------------------------------
393
397
  The `method` is the luminance calculation method to use:
@@ -496,7 +500,7 @@ class hexa:
496
500
  self.b: int
497
501
  self.a: Optional[float]
498
502
  if all(x is not None for x in (_r, _g, _b)):
499
- self.r, self.g, self.b, self.a = _r, _g, _b, _a # type: ignore[assignment]
503
+ self.r, self.g, self.b, self.a = cast(int, _r), cast(int, _g), cast(int, _b), _a
500
504
  return
501
505
  if isinstance(color, hexa):
502
506
  raise ValueError("Color is already a hexa() color")
@@ -628,7 +632,7 @@ class hexa:
628
632
  self.a = 1 - self.a
629
633
  return hexa("", self.r, self.g, self.b, self.a)
630
634
 
631
- def grayscale(self, method: str = "wcag2") -> "hexa":
635
+ def grayscale(self, method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2") -> "hexa":
632
636
  """Converts the color to grayscale using the luminance formula.\n
633
637
  ------------------------------------------------------------------
634
638
  The `method` is the luminance calculation method to use:
@@ -677,7 +681,7 @@ class hexa:
677
681
  class Color:
678
682
 
679
683
  @staticmethod
680
- def is_valid_rgba(color: Rgba, allow_alpha: bool = True) -> bool:
684
+ def is_valid_rgba(color: AnyRgba, allow_alpha: bool = True) -> bool:
681
685
  try:
682
686
  if isinstance(color, rgba):
683
687
  return True
@@ -685,7 +689,7 @@ class Color:
685
689
  if allow_alpha and Color.has_alpha(color):
686
690
  return (
687
691
  0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
688
- and (0 <= color[3] <= 1 or color[3] is None) # type: ignore[index]
692
+ and (0 <= color[3] <= 1 or color[3] is None)
689
693
  )
690
694
  elif len(color) == 3:
691
695
  return 0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
@@ -708,7 +712,7 @@ class Color:
708
712
  return False
709
713
 
710
714
  @staticmethod
711
- def is_valid_hsla(color: Hsla, allow_alpha: bool = True) -> bool:
715
+ def is_valid_hsla(color: AnyHsla, allow_alpha: bool = True) -> bool:
712
716
  try:
713
717
  if isinstance(color, hsla):
714
718
  return True
@@ -716,7 +720,7 @@ class Color:
716
720
  if allow_alpha and Color.has_alpha(color):
717
721
  return (
718
722
  0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
719
- and (0 <= color[3] <= 1 or color[3] is None) # type: ignore[index]
723
+ and (0 <= color[3] <= 1 or color[3] is None)
720
724
  )
721
725
  elif len(color) == 3:
722
726
  return 0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
@@ -734,12 +738,13 @@ class Color:
734
738
  return False
735
739
  elif isinstance(color, str):
736
740
  return bool(_re.fullmatch(Regex.hsla_str(allow_alpha=allow_alpha), color))
741
+ return False
737
742
  except Exception:
738
743
  return False
739
744
 
740
745
  @staticmethod
741
746
  def is_valid_hexa(
742
- color: Hexa,
747
+ color: AnyHexa,
743
748
  allow_alpha: bool = True,
744
749
  get_prefix: bool = False,
745
750
  ) -> bool | tuple[bool, Optional[Literal['#', '0x']]]:
@@ -754,14 +759,15 @@ class Color:
754
759
  (color[2:], "0x") if color.startswith("0x") else (color, None))
755
760
  return ((bool(_re.fullmatch(Regex.hexa_str(allow_alpha=allow_alpha), color)),
756
761
  prefix) if get_prefix else bool(_re.fullmatch(Regex.hexa_str(allow_alpha=allow_alpha), color)))
762
+ return False
757
763
  except Exception:
758
764
  return (False, None) if get_prefix else False
759
765
 
760
766
  @staticmethod
761
- def is_valid(color: Rgba | Hsla | Hexa, allow_alpha: bool = True) -> bool:
767
+ def is_valid(color: AnyRgba | AnyHsla | AnyHexa, allow_alpha: bool = True) -> bool:
762
768
  return bool(
763
- Color.is_valid_rgba(color, allow_alpha) or Color.is_valid_hsla(color, allow_alpha) # type: ignore[assignment]
764
- or Color.is_valid_hexa(color, allow_alpha) # type: ignore[assignment]
769
+ Color.is_valid_rgba(color, allow_alpha) or Color.is_valid_hsla(color, allow_alpha)
770
+ or Color.is_valid_hexa(color, allow_alpha)
765
771
  )
766
772
 
767
773
  @staticmethod
@@ -772,7 +778,7 @@ class Color:
772
778
  Returns `True` if the color has an alpha channel and `False` otherwise."""
773
779
  if isinstance(color, (rgba, hsla, hexa)):
774
780
  return color.has_alpha()
775
- if Color.is_valid_hexa(color): # type: ignore[assignment]
781
+ if Color.is_valid_hexa(color):
776
782
  if isinstance(color, str):
777
783
  if color.startswith("#"):
778
784
  color = color[1:]
@@ -791,11 +797,11 @@ class Color:
791
797
  """Will try to convert any color type to a color of type RGBA."""
792
798
  if isinstance(color, (hsla, hexa)):
793
799
  return color.to_rgba()
794
- elif Color.is_valid_hsla(color): # type: ignore[assignment]
800
+ elif Color.is_valid_hsla(color):
795
801
  return hsla(*color, _validate=False).to_rgba() # type: ignore[not-iterable]
796
- elif Color.is_valid_hexa(color): # type: ignore[assignment]
797
- return hexa(color).to_rgba() # type: ignore[assignment]
798
- elif Color.is_valid_rgba(color): # type: ignore[assignment]
802
+ elif Color.is_valid_hexa(color):
803
+ return hexa(cast(str | int, color)).to_rgba()
804
+ elif Color.is_valid_rgba(color):
799
805
  return color if isinstance(color, rgba) else (rgba(*color, _validate=False)) # type: ignore[not-iterable]
800
806
  raise ValueError(f"Invalid color format '{color}'")
801
807
 
@@ -804,11 +810,11 @@ class Color:
804
810
  """Will try to convert any color type to a color of type HSLA."""
805
811
  if isinstance(color, (rgba, hexa)):
806
812
  return color.to_hsla()
807
- elif Color.is_valid_rgba(color): # type: ignore[assignment]
813
+ elif Color.is_valid_rgba(color):
808
814
  return rgba(*color, _validate=False).to_hsla() # type: ignore[not-iterable]
809
- elif Color.is_valid_hexa(color): # type: ignore[assignment]
810
- return hexa(color).to_hsla() # type: ignore[assignment]
811
- elif Color.is_valid_hsla(color): # type: ignore[assignment]
815
+ elif Color.is_valid_hexa(color):
816
+ return hexa(cast(str | int, color)).to_hsla()
817
+ elif Color.is_valid_hsla(color):
812
818
  return color if isinstance(color, hsla) else (hsla(*color, _validate=False)) # type: ignore[not-iterable]
813
819
  raise ValueError(f"Invalid color format '{color}'")
814
820
 
@@ -817,12 +823,12 @@ class Color:
817
823
  """Will try to convert any color type to a color of type HEXA."""
818
824
  if isinstance(color, (rgba, hsla)):
819
825
  return color.to_hexa()
820
- elif Color.is_valid_rgba(color): # type: ignore[assignment]
826
+ elif Color.is_valid_rgba(color):
821
827
  return rgba(*color, _validate=False).to_hexa() # type: ignore[not-iterable]
822
- elif Color.is_valid_hsla(color): # type: ignore[assignment]
828
+ elif Color.is_valid_hsla(color):
823
829
  return hsla(*color, _validate=False).to_hexa() # type: ignore[not-iterable]
824
- elif Color.is_valid_hexa(color): # type: ignore[assignment]
825
- return color if isinstance(color, hexa) else hexa(color) # type: ignore[assignment]
830
+ elif Color.is_valid_hexa(color):
831
+ return color if isinstance(color, hexa) else hexa(cast(str | int, color))
826
832
  raise ValueError(f"Invalid color format '{color}'")
827
833
 
828
834
  @staticmethod
@@ -919,7 +925,13 @@ class Color:
919
925
  raise ValueError(f"Invalid HEX integer '0x{hex_str}': expected in range [0x000000, 0xFFFFFF]")
920
926
 
921
927
  @staticmethod
922
- def luminance(r: int, g: int, b: int, output_type: Optional[type] = None, method: str = "wcag2") -> int | float:
928
+ def luminance(
929
+ r: int,
930
+ g: int,
931
+ b: int,
932
+ output_type: Optional[type] = None,
933
+ method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2",
934
+ ) -> int | float:
923
935
  """Calculates the relative luminance of a color according to various standards.\n
924
936
  ----------------------------------------------------------------------------------
925
937
  The `output_type` controls the range of the returned luminance value:
@@ -963,7 +975,7 @@ class Color:
963
975
 
964
976
  @staticmethod
965
977
  def text_color_for_on_bg(text_bg_color: Rgba | Hexa) -> rgba | hexa | int:
966
- was_hexa, was_int = Color.is_valid_hexa(text_bg_color), isinstance(text_bg_color, int) # type: ignore[assignment]
978
+ was_hexa, was_int = Color.is_valid_hexa(text_bg_color), isinstance(text_bg_color, int)
967
979
  text_bg_color = Color.to_rgba(text_bg_color)
968
980
  brightness = 0.2126 * text_bg_color[0] + 0.7152 * text_bg_color[1] + 0.0722 * text_bg_color[2]
969
981
  return (((0xFFFFFF if was_int else hexa("", 255, 255, 255)) if was_hexa else rgba(255, 255, 255, _validate=False))
@@ -978,22 +990,22 @@ class Color:
978
990
  - lightness_change (float): float between -1.0 (darken by `100%`) and 1.0 (lighten by `100%`)\n
979
991
  -----------------------------------------------------------------------------------------------------
980
992
  returns (rgba|hexa): the adjusted color in the format of the input color"""
981
- was_hexa = Color.is_valid_hexa(color) # type: ignore[assignment]
982
- _color: hsla = Color.to_hsla(color) # type: ignore[assignment]
993
+ was_hexa = Color.is_valid_hexa(color)
994
+ _color: hsla = Color.to_hsla(color)
983
995
  h, s, l, a = (int(_color[0]), int(_color[1]), int(_color[2]), _color[3] if Color.has_alpha(_color) else None)
984
996
  l = int(max(0, min(100, l + lightness_change * 100)))
985
997
  return hsla(h, s, l, a, _validate=False).to_hexa() if was_hexa else hsla(h, s, l, a, _validate=False).to_rgba()
986
998
 
987
999
  @staticmethod
988
- def adjust_saturation(color: Rgba | Hsla | Hexa, saturation_change: float) -> rgba | hexa:
1000
+ def adjust_saturation(color: Rgba | Hexa, saturation_change: float) -> rgba | hexa:
989
1001
  """In- or decrease the saturation of the input color.\n
990
1002
  -----------------------------------------------------------------------------------------------------------
991
1003
  - color (rgba|hexa): HEX or RGBA color
992
1004
  - saturation_change (float): float between -1.0 (saturate by `100%`) and 1.0 (desaturate by `100%`)\n
993
1005
  -----------------------------------------------------------------------------------------------------------
994
1006
  returns (rgba|hexa): the adjusted color in the format of the input color"""
995
- was_hexa = Color.is_valid_hexa(color) # type: ignore[assignment]
996
- _color: hsla = Color.to_hsla(color) # type: ignore[assignment]
1007
+ was_hexa = Color.is_valid_hexa(color)
1008
+ _color: hsla = Color.to_hsla(color)
997
1009
  h, s, l, a = (int(_color[0]), int(_color[1]), int(_color[2]), _color[3] if Color.has_alpha(_color) else None)
998
1010
  s = int(max(0, min(100, s + saturation_change * 100)))
999
1011
  return hsla(h, s, l, a, _validate=False).to_hexa() if was_hexa else hsla(h, s, l, a, _validate=False).to_rgba()
xulbux/xx_console.py CHANGED
@@ -11,7 +11,7 @@ from .xx_string import String
11
11
  from .xx_color import Color, Rgba, Hexa
12
12
 
13
13
  from prompt_toolkit.key_binding.key_bindings import KeyBindings
14
- from typing import Optional, Any
14
+ from typing import Optional, Literal, Mapping, Any, cast
15
15
  import prompt_toolkit as _prompt_toolkit
16
16
  import pyperclip as _pyperclip
17
17
  import keyboard as _keyboard
@@ -48,25 +48,31 @@ class _ConsoleUser:
48
48
 
49
49
 
50
50
  class ArgResult:
51
- """Exists: if the argument was found or not\n
52
- Value: the value from behind the found argument"""
51
+ """Represents the result of a parsed command-line argument and contains the following attributes:
52
+ - `exists` -⠀if the argument was found or not
53
+ - `value` -⠀the value given with the found argument\n
54
+ --------------------------------------------------------------------------------------------------------
55
+ When the `ArgResult` instance is accessed as a boolean it will correspond to the `exists` attribute."""
53
56
 
54
57
  def __init__(self, exists: bool, value: Any):
55
- self.exists = exists
56
- self.value = value
58
+ self.exists: bool = exists
59
+ self.value: Any = value
57
60
 
58
61
  def __bool__(self):
59
62
  return self.exists
60
63
 
61
64
 
62
65
  class Args:
63
- """Stores found command arguments under their aliases with their results."""
66
+ """Container for parsed command-line arguments, allowing attribute-style access.
67
+ For example, if an argument `foo` was parsed, it can be accessed via `args.foo`.
68
+ Each such attribute (e.g. `args.foo`) is an instance of `ArgResult`."""
64
69
 
65
- def __init__(self, **kwargs):
66
- for key, value in kwargs.items():
67
- if not key.isidentifier():
68
- raise TypeError(f"Argument alias '{key}' is invalid. It must be a valid Python variable name.")
69
- setattr(self, key, ArgResult(**value))
70
+ def __init__(self, **kwargs: dict[str, Any]):
71
+ for alias_name, data_dict in kwargs.items():
72
+ if not alias_name.isidentifier():
73
+ raise TypeError(f"Argument alias '{alias_name}' is invalid. It must be a valid Python variable name.")
74
+ arg_result_instance = ArgResult(exists=data_dict["exists"], value=data_dict["value"])
75
+ setattr(self, alias_name, arg_result_instance)
70
76
 
71
77
  def __len__(self):
72
78
  return len(vars(self))
@@ -74,6 +80,9 @@ class Args:
74
80
  def __contains__(self, key):
75
81
  return hasattr(self, key)
76
82
 
83
+ def __getattr__(self, name: str) -> ArgResult:
84
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
85
+
77
86
  def __getitem__(self, key):
78
87
  if isinstance(key, int):
79
88
  return list(self.__iter__())[key]
@@ -108,14 +117,13 @@ class Console:
108
117
  h: int = _ConsoleHeight() # type: ignore[assignment]
109
118
  """The height of the console in lines."""
110
119
  wh: tuple[int, int] = _ConsoleSize() # type: ignore[assignment]
111
- """A tuple with the width and height of
112
- the console in characters and lines."""
120
+ """A tuple with the width and height of the console in characters and lines."""
113
121
  usr: str = _ConsoleUser() # type: ignore[assignment]
114
122
  """The name of the current user."""
115
123
 
116
124
  @staticmethod
117
125
  def get_args(
118
- find_args: dict[str, list[str] | tuple[str, ...] | dict[str, list[str] | tuple[str, ...] | Any]],
126
+ find_args: Mapping[str, list[str] | tuple[str, ...] | dict[str, list[str] | tuple[str, ...] | Any]],
119
127
  allow_spaces: bool = False
120
128
  ) -> Args:
121
129
  """Will search for the specified arguments in the command line
@@ -267,7 +275,11 @@ class Console:
267
275
  information about formatting codes, see `xx_format_codes` module documentation."""
268
276
  title = "" if title is None else title.strip().upper()
269
277
  title_len, tab_len = len(title) + 4, _console_tabsize - ((len(title) + 4) % _console_tabsize)
270
- title_color = "_color" if not title_bg_color else Color.text_color_for_on_bg(title_bg_color)
278
+ if title_bg_color is not None and Color.is_valid(title_bg_color):
279
+ title_bg_color = Color.to_hexa(title_bg_color)
280
+ title_color = Color.text_color_for_on_bg(title_bg_color)
281
+ else:
282
+ title_color = "_color" if title_bg_color is None else "#000"
271
283
  if format_linebreaks:
272
284
  clean_prompt, removals = FormatCodes.remove_formatting(str(prompt), get_removals=True, _ignore_linebreaks=True)
273
285
  prompt_lst = (String.split_count(l, Console.w - (title_len + tab_len)) for l in str(clean_prompt).splitlines())
@@ -275,7 +287,7 @@ class Console:
275
287
  item for lst in prompt_lst for item in ([""] if lst == [] else (lst if isinstance(lst, list) else [lst]))
276
288
  )
277
289
  prompt = f"\n{' ' * title_len}\t".join(
278
- Console.__add_back_removed_parts(list(prompt_lst), removals) # type: ignore[assignment]
290
+ Console.__add_back_removed_parts(list(prompt_lst), cast(tuple[tuple[int, str], ...], removals))
279
291
  )
280
292
  else:
281
293
  prompt = str(prompt)
@@ -427,18 +439,18 @@ class Console:
427
439
  Console.pause_exit(pause, exit, reset_ansi=reset_ansi)
428
440
 
429
441
  @staticmethod
430
- def log_box(
442
+ def log_box_filled(
431
443
  *values: object,
432
444
  start: str = "",
433
445
  end: str = "\n",
434
- box_bg_color: str | Rgba | Hexa = "green",
446
+ box_bg_color: Rgba | Hexa = "green",
435
447
  default_color: Rgba | Hexa = "#000",
436
448
  w_padding: int = 2,
437
449
  w_full: bool = False,
438
450
  ) -> None:
439
- """Will print a box, containing a formatted log message:
451
+ """Will print a box with a colored background, containing a formatted log message:
440
452
  - `*values` -⠀the box content (each value is on a new line)
441
- - `start` -⠀something to print before the log box is printed
453
+ - `start` -⠀something to print before the log box is printed (e.g. `\\n`)
442
454
  - `end` -⠀something to print after the log box is printed (e.g. `\\n`)
443
455
  - `box_bg_color` -⠀the background color of the box
444
456
  - `default_color` -⠀the default text color of the `*values`
@@ -447,24 +459,101 @@ class Console:
447
459
  -----------------------------------------------------------------------------------
448
460
  The box content can be formatted with special formatting codes. For more detailed
449
461
  information about formatting codes, see `xx_format_codes` module documentation."""
450
- lines = [line.rstrip() for val in values for line in str(val).splitlines()]
451
- unfmt_lines = [FormatCodes.remove_formatting(line) for line in lines]
452
- max_line_len = max(len(line) for line in unfmt_lines)
462
+ lines, unfmt_lines, max_line_len = Console.__prepare_log_box(values, default_color)
453
463
  pad_w_full = (Console.w - (max_line_len + (2 * w_padding))) if w_full else 0
464
+ if box_bg_color is not None and Color.is_valid(box_bg_color):
465
+ box_bg_color = Color.to_hexa(box_bg_color)
454
466
  lines = [
455
467
  f"[bg:{box_bg_color}]{' ' * w_padding}{line}" + " " *
456
- ((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[_bg]" for line, unfmt in zip(lines, unfmt_lines)
468
+ ((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[*]" for line, unfmt in zip(lines, unfmt_lines)
457
469
  ]
458
470
  pady = " " * (Console.w if w_full else max_line_len + (2 * w_padding))
459
471
  FormatCodes.print(
460
- f"{start}[bg:{box_bg_color}]{pady}[_bg]\n"
472
+ f"{start}[bg:{box_bg_color}]{pady}[*]\n"
461
473
  + _COMPILED["formatting"].sub(lambda m: f"{m.group(0)}[bg:{box_bg_color}]", "\n".join(lines))
462
- + f"\n[bg:{box_bg_color}]{pady}[_bg]",
474
+ + f"\n[bg:{box_bg_color}]{pady}[_]",
475
+ default_color=default_color or "#000",
476
+ sep="\n",
477
+ end=end,
478
+ )
479
+
480
+ @staticmethod
481
+ def log_box_bordered(
482
+ *values: object,
483
+ start: str = "",
484
+ end: str = "\n",
485
+ border_type: Literal["standard", "rounded", "strong", "double"] = "rounded",
486
+ border_style: str | Rgba | Hexa = f"dim|{COLOR.gray}",
487
+ default_color: Optional[Rgba | Hexa] = None,
488
+ w_padding: int = 1,
489
+ w_full: bool = False,
490
+ _border_chars: Optional[tuple[str, str, str, str, str, str, str, str]] = None,
491
+ ) -> None:
492
+ """Will print a bordered box, containing a formatted log message:
493
+ - `*values` -⠀the box content (each value is on a new line)
494
+ - `start` -⠀something to print before the log box is printed (e.g. `\\n`)
495
+ - `end` -⠀something to print after the log box is printed (e.g. `\\n`)
496
+ - `border_type` -⠀one of the predefined border character sets
497
+ - `default_color` -⠀the default text color of the `*values`
498
+ - `w_padding` -⠀the horizontal padding (in chars) to the box content
499
+ - `w_full` -⠀whether to make the box be the full console width or not
500
+ - `_border_chars` -⠀define your own border characters set (overwrites `border_type`)\n
501
+ ---------------------------------------------------------------------------------------
502
+ The box content can be formatted with special formatting codes. For more detailed
503
+ information about formatting codes, see `xx_format_codes` module documentation.\n
504
+ ---------------------------------------------------------------------------------------
505
+ The `border_type` can be one of the following:
506
+ - `"standard" = ('┌', '─', '┐', '│', '┘', '─', '└', '│')`
507
+ - `"rounded" = ('╭', '─', '╮', '│', '╯', '─', '╰', '│')`
508
+ - `"strong" = ('┏', '━', '┓', '┃', '┛', '━', '┗', '┃')`
509
+ - `"double" = ('╔', '═', '╗', '║', '╝', '═', '╚', '║')`\n
510
+ The order of the characters is always:
511
+ 1. top-left corner
512
+ 2. top border
513
+ 3. top-right corner
514
+ 4. right border
515
+ 5. bottom-right corner
516
+ 6. bottom border
517
+ 7. bottom-left corner
518
+ 8. left border"""
519
+ borders = {
520
+ "standard": ('┌', '─', '┐', '│', '┘', '─', '└', '│'),
521
+ "rounded": ('╭', '─', '╮', '│', '╯', '─', '╰', '│'),
522
+ "strong": ('┏', '━', '┓', '┃', '┛', '━', '┗', '┃'),
523
+ "double": ('╔', '═', '╗', '║', '╝', '═', '╚', '║'),
524
+ }
525
+ border_chars = borders.get(border_type, borders["standard"]) if _border_chars is None else _border_chars
526
+ lines, unfmt_lines, max_line_len = Console.__prepare_log_box(values, default_color)
527
+ print(unfmt_lines)
528
+ pad_w_full = (Console.w - (max_line_len + (2 * w_padding)) - (len(border_chars[1] * 2))) if w_full else 0
529
+ if border_style is not None and Color.is_valid(border_style):
530
+ border_style = Color.to_hexa(border_style)
531
+ border_l = f"[{border_style}]{border_chars[7]}[*]"
532
+ border_r = f"[{border_style}]{border_chars[3]}[_]"
533
+ lines = [
534
+ f"{border_l}{' ' * w_padding}{line}[_]" + " " * ((w_padding + max_line_len - len(unfmt)) + pad_w_full) + border_r
535
+ for line, unfmt in zip(lines, unfmt_lines)
536
+ ]
537
+ border_t = f"[{border_style}]{border_chars[0]}{border_chars[1] * (Console.w - (len(border_chars[1] * 2)) if w_full else max_line_len + (2 * w_padding))}{border_chars[2]}[_]"
538
+ border_b = f"[{border_style}]{border_chars[6]}{border_chars[5] * (Console.w - (len(border_chars[1] * 2)) if w_full else max_line_len + (2 * w_padding))}{border_chars[4]}[_]"
539
+ FormatCodes.print(
540
+ f"{start}{border_t}[_]\n" + "\n".join(lines) + f"\n{border_b}[_]",
463
541
  default_color=default_color,
464
542
  sep="\n",
465
543
  end=end,
466
544
  )
467
545
 
546
+ @staticmethod
547
+ def __prepare_log_box(
548
+ values: tuple[object, ...],
549
+ default_color: Optional[Rgba | Hexa] = None,
550
+ ) -> tuple[list[str], list[tuple[str, tuple[tuple[int, str], ...]]], int]:
551
+ """Prepares the log box content and returns it along with the max line length."""
552
+ lines = [line for val in values for line in str(val).splitlines()]
553
+ unfmt_lines = [FormatCodes.remove_formatting(line, default_color) for line in lines]
554
+ max_line_len = max(len(line) for line in unfmt_lines)
555
+ return lines, cast(list[tuple[str, tuple[tuple[int, str], ...]]], unfmt_lines), max_line_len
556
+
468
557
  @staticmethod
469
558
  def confirm(
470
559
  prompt: object = "Do you want to continue?",
@@ -494,7 +583,7 @@ class Console:
494
583
  end="\n",
495
584
  default_color: Rgba | Hexa = COLOR.cyan,
496
585
  show_keybindings=True,
497
- input_prefix=" ",
586
+ input_prefix=" ",
498
587
  reset_ansi=True,
499
588
  ) -> str:
500
589
  """An input where users can input (and paste) text over multiple lines.\n
xulbux/xx_format_codes.py CHANGED
@@ -133,7 +133,8 @@ the formatting code:
133
133
  #### Additional Formatting Codes when a `default_color` is set
134
134
 
135
135
  1. `[*]` resets everything, just like `[_]`, but the text color will remain in `default_color`
136
- 2. `[*color]` will reset the text color, just like `[_color]`, but then also make it `default_color`
136
+ (*if no `default_color` it resets everything, including the text color*)
137
+ 2. `[*color]` `[*c]` will reset the text color, just like `[_color]`, but then also make it `default_color`
137
138
  3. `[default]` will just color the text in `default_color`
138
139
  4. `[background:default]` `[BG:default]` will color the background in `default_color`
139
140
 
@@ -155,7 +156,7 @@ from .xx_string import String
155
156
  from .xx_regex import Regex, Match, Pattern
156
157
  from .xx_color import Color, rgba, Rgba, Hexa
157
158
 
158
- from typing import Optional
159
+ from typing import Optional, cast
159
160
  import ctypes as _ctypes
160
161
  import regex as _rx
161
162
  import sys as _sys
@@ -174,7 +175,7 @@ _PREFIX_RX: dict[str, str] = {
174
175
  }
175
176
  _COMPILED: dict[str, Pattern] = { # PRECOMPILE REGULAR EXPRESSIONS
176
177
  "*": _re.compile(r"\[\s*([^]_]*?)\s*\*\s*([^]_]*?)\]"),
177
- "*color": _re.compile(r"\[\s*([^]_]*?)\s*\*color\s*([^]_]*?)\]"),
178
+ "*color": _re.compile(r"\[\s*([^]_]*?)\s*\*c(?:olor)?\s*([^]_]*?)\]"),
178
179
  "ansi_seq": _re.compile(ANSI.char + r"(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"),
179
180
  "formatting": _rx.compile(
180
181
  Regex.brackets("[", "]", is_group=True, ignore_in_strings=False)
@@ -253,15 +254,18 @@ class FormatCodes:
253
254
  `xx_format_codes` module documentation."""
254
255
  if not isinstance(string, str):
255
256
  string = str(string)
256
- if default_color and Color.is_valid_rgba(default_color, False): # type: ignore[assignment]
257
+ if default_color and Color.is_valid_rgba(default_color, False):
257
258
  use_default = True
258
- elif default_color and Color.is_valid_hexa(default_color, False): # type: ignore[assignment]
259
+ elif default_color and Color.is_valid_hexa(default_color, False):
259
260
  use_default, default_color = True, Color.to_rgba(default_color)
260
261
  else:
261
262
  use_default = False
263
+ default_color = cast(rgba, default_color) if use_default else None
262
264
  if use_default:
263
265
  string = _COMPILED["*"].sub(r"[\1_|default\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|default|…]`
264
- string = _COMPILED["*color"].sub(r"[\1default\2]", string) # REPLACE `[…|*color|…]` WITH `[…|default|…]`
266
+ string = _COMPILED["*color"].sub(r"[\1default\2]", string) # REPLACE `[…|*color|…]` OR `[…|*c|…]` WITH `[…|default|…]`
267
+ else:
268
+ string = _COMPILED["*"].sub(r"[\1_\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|…]`
265
269
 
266
270
  def is_valid_color(color: str) -> bool:
267
271
  return bool((color in ANSI.color_map) or Color.is_valid_rgba(color) or Color.is_valid_hexa(color))
@@ -280,8 +284,8 @@ class FormatCodes:
280
284
  formats = FormatCodes.to_ansi(formats, default_color, brightness_steps, False)
281
285
  format_keys = [k.strip() for k in formats.split("|") if k.strip()]
282
286
  ansi_formats = [
283
- r if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)) != k # type: ignore[assignment]
284
- else f"[{k}]" for k in format_keys
287
+ r if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)) != k else f"[{k}]"
288
+ for k in format_keys
285
289
  ]
286
290
  if auto_reset_txt and not auto_reset_escaped:
287
291
  reset_keys = []
@@ -292,7 +296,7 @@ class FormatCodes:
292
296
  if k_set & _PREFIX["BR"]:
293
297
  for i in range(len(k)):
294
298
  if is_valid_color(k[i:]):
295
- reset_keys.extend(["_bg", "_color"])
299
+ reset_keys.extend(["_bg", "default"] if use_default else ["_bg", "_c"])
296
300
  break
297
301
  else:
298
302
  for i in range(len(k)):
@@ -302,13 +306,12 @@ class FormatCodes:
302
306
  elif is_valid_color(k) or any(
303
307
  k_lower.startswith(pref_colon := f"{prefix}:") and is_valid_color(k[len(pref_colon):])
304
308
  for prefix in _PREFIX["BR"]):
305
- reset_keys.append("_color")
309
+ reset_keys.append("default" if use_default else "_c")
306
310
  else:
307
311
  reset_keys.append(f"_{k}")
308
312
  ansi_resets = [
309
- r for k in reset_keys
310
- if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps) # type: ignore[assignment]
311
- ).startswith(f"{ANSI.char}{ANSI.start}")
313
+ r for k in reset_keys if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)
314
+ ).startswith(f"{ANSI.char}{ANSI.start}")
312
315
  ]
313
316
  else:
314
317
  ansi_resets = []
@@ -326,8 +329,8 @@ class FormatCodes:
326
329
  )
327
330
 
328
331
  string = "\n".join(_COMPILED["formatting"].sub(replace_keys, line) for line in string.split("\n"))
329
- return (((FormatCodes.__get_default_ansi(default_color) or "") if _default_start else "") # type: ignore[assignment]
330
- + string) if use_default else string
332
+ return (((FormatCodes.__get_default_ansi(default_color.values()) or "") if _default_start else "")
333
+ + string) if default_color is not None else string
331
334
 
332
335
  @staticmethod
333
336
  def escape_ansi(ansi_string: str) -> str:
@@ -366,6 +369,7 @@ class FormatCodes:
366
369
  @staticmethod
367
370
  def remove_formatting(
368
371
  string: str,
372
+ default_color: Optional[Rgba | Hexa] = None,
369
373
  get_removals: bool = False,
370
374
  _ignore_linebreaks: bool = False,
371
375
  ) -> str | tuple[str, tuple[tuple[int, str], ...]]:
@@ -375,7 +379,7 @@ class FormatCodes:
375
379
  Each tuple contains the position of the removed formatting code and the removed formatting code.\n
376
380
  If `_ignore_linebreaks` is true, linebreaks will be ignored for the removal positions."""
377
381
  return FormatCodes.remove_ansi(
378
- FormatCodes.to_ansi(string),
382
+ FormatCodes.to_ansi(string, default_color=default_color),
379
383
  get_removals=get_removals,
380
384
  _ignore_linebreaks=_ignore_linebreaks,
381
385
  )
@@ -395,7 +399,7 @@ class FormatCodes:
395
399
 
396
400
  @staticmethod
397
401
  def __get_default_ansi(
398
- default_color: tuple,
402
+ default_color: tuple[int, int, int],
399
403
  format_key: Optional[str] = None,
400
404
  brightness_steps: Optional[int] = None,
401
405
  _modifiers: tuple[str, str] = (ANSI.default_color_modifiers["lighten"], ANSI.default_color_modifiers["darken"]),
@@ -428,12 +432,12 @@ class FormatCodes:
428
432
  return (ANSI.seq_bg_color if is_bg else ANSI.seq_color).format(*new_rgb[:3])
429
433
 
430
434
  @staticmethod
431
- def __get_replacement(format_key: str, default_color: Optional[Rgba] = None, brightness_steps: int = 20) -> str:
435
+ def __get_replacement(format_key: str, default_color: Optional[rgba], brightness_steps: int = 20) -> str:
432
436
  """Gives you the corresponding ANSI code for the given format key.
433
437
  If `default_color` is not `None`, the text color will be `default_color` if all formats
434
438
  are reset or you can get lighter or darker version of `default_color` (also as BG)"""
435
439
  use_default = default_color and Color.is_valid_rgba(default_color, False)
436
- _default_color = tuple(Color.to_rgba(default_color)) if use_default else () # type: ignore[assignment]
440
+ _default_color = tuple(Color.to_rgba(default_color)) if default_color is not None else tuple()
437
441
  _format_key, format_key = format_key, FormatCodes.__normalize_key(format_key) # NORMALIZE KEY AND SAVE ORIGINAL
438
442
  if use_default:
439
443
  if new_default_color := FormatCodes.__get_default_ansi(_default_color, format_key, brightness_steps):
xulbux/xx_regex.py CHANGED
@@ -11,13 +11,13 @@ class Regex:
11
11
 
12
12
  @staticmethod
13
13
  def quotes() -> str:
14
- """Matches everything inside quotes. (strings)\n
14
+ """Matches pairs of quotes. (strings)\n
15
15
  --------------------------------------------------------------------------------
16
16
  Will create two named groups:
17
17
  - `quote` the quote type (single or double)
18
18
  - `string` everything inside the found quote pair\n
19
- --------------------------------------------------------------------------------
20
- Attention: Requires non standard library `regex` not standard library `re`!"""
19
+ ---------------------------------------------------------------------------------
20
+ Attention: Requires non-standard library `regex`, not standard library `re`!"""
21
21
  return r'(?P<quote>[\'"])(?P<string>(?:\\.|(?!\g<quote>).)*?)\g<quote>'
22
22
 
23
23
  @staticmethod
@@ -28,16 +28,16 @@ class Regex:
28
28
  strip_spaces: bool = True,
29
29
  ignore_in_strings: bool = True,
30
30
  ) -> str:
31
- """Matches everything inside brackets, including other nested brackets.\n
32
- --------------------------------------------------------------------------------
31
+ """Matches everything inside pairs of brackets, including other nested brackets.\n
32
+ -----------------------------------------------------------------------------------
33
33
  If `is_group` is true, you will be able to reference the matched content as a
34
34
  group (e.g. `match.group(…)` or `r'\\…'`).
35
35
  If `strip_spaces` is true, it will ignore spaces around the content inside the
36
36
  brackets.
37
37
  If `ignore_in_strings` is true and a bracket is inside a string (e.g. `'...'`
38
38
  or `"..."`), it will not be counted as the matching closing bracket.\n
39
- --------------------------------------------------------------------------------
40
- Attention: Requires non standard library `regex` not standard library `re`!"""
39
+ -----------------------------------------------------------------------------------
40
+ Attention: Requires non-standard library `regex`, not standard library `re`!"""
41
41
  g, b1, b2, s1, s2 = (
42
42
  "" if is_group else "?:",
43
43
  _rx.escape(bracket1) if len(bracket1) == 1 else bracket1,
@@ -73,8 +73,8 @@ class Regex:
73
73
  1. function name
74
74
  2. the function's arguments\n
75
75
  If no `func_name` is given, it will match any function call.\n
76
- --------------------------------------------------------------------------------
77
- Attention: Requires non standard library `regex` not standard library `re`!"""
76
+ ---------------------------------------------------------------------------------
77
+ Attention: Requires non-standard library `regex`, not standard library `re`!"""
78
78
  return (
79
79
  r"(?<=\b)(" + (r"[\w_]+" if func_name is None else func_name) + r")\s*" + Regex.brackets("(", ")", is_group=True)
80
80
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xulbux
3
- Version: 1.7.0
3
+ Version: 1.7.1
4
4
  Summary: A Python library which includes lots of helpful classes, types and functions aiming to make common programming tasks simpler.
5
5
  Author-email: XulbuX <xulbux.real@gmail.com>
6
6
  License-Expression: MIT
@@ -34,14 +34,14 @@ Requires-Dist: flake8>=6.1.0; extra == "dev"
34
34
  Requires-Dist: flake8-pyproject>=1.2.3; extra == "dev"
35
35
  Dynamic: license-file
36
36
 
37
- # **$\color{#8085FF}\Huge\textsf{XulbuX}$**
37
+ # **$\Huge\textsf{XulbuX}$**
38
38
 
39
- **$\color{#8085FF}\textsf{XulbuX}$** is library that contains many useful classes, types, and functions,
39
+ **XulbuX** is library that contains many useful classes, types, and functions,
40
40
  ranging from console logging and working with colors to file management and system operations.
41
41
  The library is designed to simplify common programming tasks and improve code readability through its collection of tools.
42
42
 
43
- For precise information about the library, see the library's [wiki page](https://github.com/XulbuX/PythonLibraryXulbuX/wiki).<br>
44
- For the libraries latest changes and updates, see the [change log](https://github.com/XulbuX/PythonLibraryXulbuX/blob/main/CHANGELOG.md).
43
+ For precise information about the library, see the library's [**wiki page**](https://github.com/XulbuX/PythonLibraryXulbuX/wiki).<br>
44
+ For the libraries latest changes and updates, see the [**change log**](https://github.com/XulbuX/PythonLibraryXulbuX/blob/main/CHANGELOG.md).
45
45
 
46
46
  <br>
47
47
 
@@ -81,20 +81,20 @@ from xulbux import rgba, hsla, hexa
81
81
 
82
82
  ## Modules
83
83
 
84
- | Module | Short Description |
85
- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- |
86
- | [![xx_code](https://img.shields.io/badge/xx__code-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_code) | advanced code-string operations (*changing the indent, finding function calls, ...*) |
87
- | [![xx_color](https://img.shields.io/badge/xx__color-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_color) | everything around colors (*converting, blending, searching colors in strings, ...*) |
88
- | [![xx_console](https://img.shields.io/badge/xx__console-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_console) | advanced actions related to the console (*pretty logging, advanced inputs, ...*) |
89
- | [![xx_data](https://img.shields.io/badge/xx__data-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_data) | advanced operations with data structures (*compare, generate path ID's, pretty print/format, ...*) |
84
+ | Module | Short Description |
85
+ | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- |
86
+ | [![xx_code](https://img.shields.io/badge/xx__code-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_code) | advanced code-string operations (*changing the indent, finding function calls, ...*) |
87
+ | [![xx_color](https://img.shields.io/badge/xx__color-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_color) | everything around colors (*converting, blending, searching colors in strings, ...*) |
88
+ | [![xx_console](https://img.shields.io/badge/xx__console-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_console) | advanced actions related to the console (*pretty logging, advanced inputs, ...*) |
89
+ | [![xx_data](https://img.shields.io/badge/xx__data-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_data) | advanced operations with data structures (*compare, generate path ID's, pretty print/format, ...*) |
90
90
  | [![xx_env_path](https://img.shields.io/badge/xx__env__path-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_env_path) | getting and editing the PATH variable (*get paths, check for paths, add paths, ...*) |
91
- | [![xx_file](https://img.shields.io/badge/xx__file-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_file) | advanced working with files (*create files, rename file-extensions, ...*) |
91
+ | [![xx_file](https://img.shields.io/badge/xx__file-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_file) | advanced working with files (*create files, rename file-extensions, ...*) |
92
92
  | [![xx_format_codes](https://img.shields.io/badge/xx__format__codes-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_format_codes) | easy pretty printing with custom format codes (*print, inputs, custom format codes to ANSI, ...*) |
93
- | [![xx_json](https://img.shields.io/badge/xx__json-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_json) | advanced working with json files (*read, create, update, ...*) |
94
- | [![xx_path](https://img.shields.io/badge/xx__path-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_path) | advanced path operations (*get paths, smart-extend relative paths, delete paths, ...*) |
95
- | ![xx_regex](https://img.shields.io/badge/xx__regex-6065FF?style=flat) | generated regex pattern-templates (*match bracket- and quote pairs, match colors, ...*) |
96
- | [![xx_string](https://img.shields.io/badge/xx__string-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_string) | helpful actions when working with strings. (*normalize, escape, decompose, ...*) |
97
- | ![xx_system](https://img.shields.io/badge/xx__system-6065FF?style=flat) | advanced system actions (*restart with message, check installed Python libs, ...*) |
93
+ | [![xx_json](https://img.shields.io/badge/xx__json-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_json) | advanced working with json files (*read, create, update, ...*) |
94
+ | [![xx_path](https://img.shields.io/badge/xx__path-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_path) | advanced path operations (*get paths, smart-extend relative paths, delete paths, ...*) |
95
+ | ![xx_regex](https://img.shields.io/badge/xx__regex-6065FF?style=flat) | generated regex pattern-templates (*match bracket- and quote pairs, match colors, ...*) |
96
+ | [![xx_string](https://img.shields.io/badge/xx__string-6065FF?style=flat)](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_string) | helpful actions when working with strings. (*normalize, escape, decompose, ...*) |
97
+ | ![xx_system](https://img.shields.io/badge/xx__system-6065FF?style=flat) | advanced system actions (*restart with message, check installed Python libs, ...*) |
98
98
 
99
99
  <br>
100
100
 
@@ -111,15 +111,15 @@ def main() -> None:
111
111
 
112
112
  # LET THE USER ENTER A HEXA COLOR IN ANY HEXA FORMAT
113
113
  input_clr = FormatCodes.input(
114
- "\n[b](Enter a HEXA color in any format) [dim](>) "
114
+ "\n[b](Enter a HEXA color in any format) [dim](>) "
115
115
  )
116
116
 
117
117
  # ANNOUNCE INDEXING THE INPUT COLOR
118
118
  Console.log(
119
- "INDEX",
120
- "Indexing the input HEXA color...",
121
- start="\n",
122
- title_bg_color=COLOR.blue,
119
+ "INDEX",
120
+ "Indexing the input HEXA color...",
121
+ start="\n",
122
+ title_bg_color=COLOR.blue,
123
123
  )
124
124
 
125
125
  try:
@@ -129,16 +129,16 @@ def main() -> None:
129
129
  except ValueError:
130
130
  # ANNOUNCE THE ERROR AND EXIT THE PROGRAM
131
131
  Console.fail(
132
- "The input HEXA color is invalid.",
133
- end="\n\n",
134
- exit=True,
132
+ "The input HEXA color is invalid.",
133
+ end="\n\n",
134
+ exit=True,
135
135
  )
136
136
 
137
137
  # ANNOUNCE STARTING THE CONVERSION
138
138
  Console.log(
139
- "CONVERT",
140
- "Converting the HEXA color into different types...",
141
- title_bg_color=COLOR.tangerine,
139
+ "CONVERT",
140
+ "Converting the HEXA color into different types...",
141
+ title_bg_color=COLOR.tangerine,
142
142
  )
143
143
 
144
144
  # CONVERT THE HEXA COLOR INTO THE TWO OTHER COLOR TYPES
@@ -147,14 +147,16 @@ def main() -> None:
147
147
 
148
148
  # ANNOUNCE THE SUCCESSFUL CONVERSION
149
149
  Console.done(
150
- "Successfully converted color into different types.",
151
- end="\n\n",
150
+ "Successfully converted color into different types.",
151
+ end="\n\n",
152
152
  )
153
153
 
154
154
  # PRETTY PRINT THE COLOR IN DIFFERENT TYPES
155
- FormatCodes.print(f"[b](HEXA:) [i|white]({hexa_color})")
156
- FormatCodes.print(f"[b](RGBA:) [i|white]({rgba_color})")
157
- FormatCodes.print(f"[b](HSLA:) [i|white]({hsla_color})\n")
155
+ Console.log_box_bordered(
156
+ f"[b](HEXA:) [i|white]({hexa_color})",
157
+ f"[b](RGBA:) [i|white]({rgba_color})",
158
+ f"[b](HSLA:) [i|white]({hsla_color})",
159
+ )
158
160
 
159
161
 
160
162
  if __name__ == "__main__":
@@ -165,4 +167,4 @@ if __name__ == "__main__":
165
167
  <br>
166
168
 
167
169
  --------------------------------------------------------------
168
- [View this library on PyPI](https://pypi.org/project/XulbuX/)
170
+ [View this library on **PyPI**](https://pypi.org/project/XulbuX/)
@@ -1,21 +1,21 @@
1
- xulbux/__init__.py,sha256=TbXPioXPENwpR9t4lvqBBhV4cby0JGdIrSzzdMFYrEc,815
1
+ xulbux/__init__.py,sha256=4qceHbARrYX-ui-kl5bufH3BlEvMwJpsXG1TQsbNHgA,815
2
2
  xulbux/_cli_.py,sha256=J4vfJHLJEYxCZzA_VJUB46w2WGShfdYFoetsLG5PfKo,3428
3
3
  xulbux/_consts_.py,sha256=AuYTTmqrP2lawyVGlPLUaP1syxOoPA-ejJGH7WlwFzk,6314
4
4
  xulbux/xx_code.py,sha256=w9yO-GPMeaE-xDi-L3VtpPpWpu5jOwagfMsG93aXANE,6106
5
- xulbux/xx_color.py,sha256=ZEV9AG3MtgNMh4t4VexmBiaperan1pHO4d_VPCdv188,49923
6
- xulbux/xx_console.py,sha256=WmH6YURaA-Y-LNB-2kZc8SuPxoZim8ZmzsuI01V9vcM,28682
5
+ xulbux/xx_color.py,sha256=Thj7fFTc8x-VmYyULI3HQW0uCk9dIihwrGUwuXGn83s,49744
6
+ xulbux/xx_console.py,sha256=mruEcbehVceXuDhIMV_WoFec8MxXf2orvlNQWkdI-zE,34367
7
7
  xulbux/xx_data.py,sha256=nEfVwK6-ILaL3K-bLezKpG1G7117CY5ZgC3BGwANrUE,30886
8
8
  xulbux/xx_env_path.py,sha256=x56mKK4lSvU5yMCAs8k0RVIqXWUJcpcHYz5HoZ_RklM,4160
9
9
  xulbux/xx_file.py,sha256=KerXOvKS93zIoAt36YTYuZboSmxVFVf2WcOrDcdwXfE,2627
10
- xulbux/xx_format_codes.py,sha256=fOFidFDBYV-rUXOlYTeKIZarfh9Q1jSUCGYyk8pg-ic,23397
10
+ xulbux/xx_format_codes.py,sha256=NihbRkPjDSWJCL2itZBlvhbO0bWx8Mm2P-LpSeaG3po,23732
11
11
  xulbux/xx_json.py,sha256=V7vdfpvSe9wpktR_c8zG_Meix7x9IRmn66k5nB3HUyo,7457
12
12
  xulbux/xx_path.py,sha256=lLAEVZrW0TAwCewlONFVQcQ_8tVn9LTJZVOZpeGvE5s,7673
13
- xulbux/xx_regex.py,sha256=ejqVs4a-eCSTrSQfENoyl0AVztb8BuI2TNl-wzMQwYw,8048
13
+ xulbux/xx_regex.py,sha256=_BtMHRDNcD9zF4SL87dQuUVZcYGfZx9H5YNSDiEtzm8,8059
14
14
  xulbux/xx_string.py,sha256=QaTo0TQ9m_2USNgQNaVw5ivQt-A1E-e5x8OpIB3xIlY,5561
15
15
  xulbux/xx_system.py,sha256=Tsx4wgztUg46KloqcGeiFkarDoM3EgJLXw3XNxgHBmU,6460
16
- xulbux-1.7.0.dist-info/licenses/LICENSE,sha256=6NflEcvzFEe8_JFVNCPVwZBwBhlLLd4vqQi8WiX_Xk4,1084
17
- xulbux-1.7.0.dist-info/METADATA,sha256=symGYQzZhSYyGcwM_dGI3FETAcqgTL-ft90CLtJoNlY,9222
18
- xulbux-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- xulbux-1.7.0.dist-info/entry_points.txt,sha256=a3womfLIMZKnOFiyy-xnVb4g2qkZsHR5FbKKkljcGns,94
20
- xulbux-1.7.0.dist-info/top_level.txt,sha256=FkK4EZajwfP36fnlrPaR98OrEvZpvdEOdW1T5zTj6og,7
21
- xulbux-1.7.0.dist-info/RECORD,,
16
+ xulbux-1.7.1.dist-info/licenses/LICENSE,sha256=6NflEcvzFEe8_JFVNCPVwZBwBhlLLd4vqQi8WiX_Xk4,1084
17
+ xulbux-1.7.1.dist-info/METADATA,sha256=rMdO8mHMOtT-oLsysIUup9myW_B93Z2UdG1Gvasu_rA,9209
18
+ xulbux-1.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ xulbux-1.7.1.dist-info/entry_points.txt,sha256=a3womfLIMZKnOFiyy-xnVb4g2qkZsHR5FbKKkljcGns,94
20
+ xulbux-1.7.1.dist-info/top_level.txt,sha256=FkK4EZajwfP36fnlrPaR98OrEvZpvdEOdW1T5zTj6og,7
21
+ xulbux-1.7.1.dist-info/RECORD,,
File without changes