mdify-cli 3.1.0__tar.gz → 3.2.0__tar.gz

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.
Files changed (28) hide show
  1. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/PKG-INFO +1 -1
  2. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/__init__.py +1 -1
  3. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/cli.py +76 -56
  4. mdify_cli-3.2.0/mdify/formatting.py +138 -0
  5. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify_cli.egg-info/PKG-INFO +1 -1
  6. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/pyproject.toml +1 -1
  7. mdify_cli-3.1.0/mdify/formatting.py +0 -29
  8. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/LICENSE +0 -0
  9. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/README.md +0 -0
  10. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/assets/mdify.png +0 -0
  11. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/__main__.py +0 -0
  12. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/container.py +0 -0
  13. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/docling_client.py +0 -0
  14. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/ssh/__init__.py +0 -0
  15. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/ssh/client.py +0 -0
  16. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/ssh/models.py +0 -0
  17. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/ssh/remote_container.py +0 -0
  18. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify/ssh/transfer.py +0 -0
  19. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify_cli.egg-info/SOURCES.txt +0 -0
  20. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify_cli.egg-info/dependency_links.txt +0 -0
  21. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify_cli.egg-info/entry_points.txt +0 -0
  22. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify_cli.egg-info/requires.txt +0 -0
  23. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/mdify_cli.egg-info/top_level.txt +0 -0
  24. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/setup.cfg +0 -0
  25. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/tests/test_cli.py +0 -0
  26. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/tests/test_container.py +0 -0
  27. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/tests/test_docling_client.py +0 -0
  28. {mdify_cli-3.1.0 → mdify_cli-3.2.0}/tests/test_ssh_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdify-cli
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Convert PDFs and document images into structured Markdown for LLM workflows
5
5
  Author: tiroq
6
6
  License-Expression: MIT
@@ -1,3 +1,3 @@
1
1
  """mdify - Convert documents to Markdown via Docling container."""
2
2
 
3
- __version__ = "3.1.0"
3
+ __version__ = "3.2.0"
@@ -275,17 +275,20 @@ def check_for_update(force: bool = False) -> None:
275
275
 
276
276
  if not _compare_versions(__version__, remote_version):
277
277
  if force:
278
- print(f"mdify is up to date (version {__version__})")
278
+ from mdify.formatting import Colorizer
279
+ color = Colorizer(sys.stdout)
280
+ print(color.success(f"✓ mdify is up to date (v{__version__})"))
279
281
  return
280
282
 
281
- print(f"\n{'=' * 50}")
282
- print(f"A new version of mdify-cli is available!")
283
- print(f" Current version: {__version__}")
284
- print(f" Latest version: {remote_version}")
285
- print(f"{'=' * 50}")
286
- print(f"\nTo upgrade, run:")
287
- print(f" pipx upgrade mdify-cli")
288
- print(f" # or: pip install --upgrade mdify-cli\n")
283
+ from mdify.formatting import Colorizer
284
+ color = Colorizer(sys.stdout)
285
+ print(f"\n{color.bright_yellow('=' * 60)}")
286
+ print(color.bold_yellow("🎉 A new version of mdify-cli is available!"))
287
+ print(f"{color.dim_white(' Current:')} {__version__} → {color.bright_green(remote_version)}")
288
+ print(f"{color.bright_yellow('=' * 60)}")
289
+ print(f"\n{color.cyan('To upgrade, run:')}")
290
+ print(f" {color.bold('pipx upgrade mdify-cli')}")
291
+ print(f" {color.dim_white('# or: pip install --upgrade mdify-cli')}\n")
289
292
 
290
293
 
291
294
  # =============================================================================
@@ -1113,57 +1116,57 @@ def main_async_remote(args) -> int:
1113
1116
 
1114
1117
  # Connect to remote server
1115
1118
  if not args.quiet:
1116
- print(color.cyan(f"Connecting to {ssh_config.host}:{ssh_config.port}..."), file=sys.stderr)
1119
+ print(color.bright_cyan(f"🔗 Connecting to {color.bold(ssh_config.host)}:{ssh_config.port}..."), file=sys.stderr)
1117
1120
 
1118
1121
  await ssh_client.connect()
1119
1122
 
1120
1123
  if not args.quiet:
1121
- print(color.green(f"✓ Connected to {ssh_config.host}"), file=sys.stderr)
1124
+ print(color.success(f"✓ Connected to {ssh_config.host}"), file=sys.stderr)
1122
1125
 
1123
1126
  # Validate remote resources if not skipped
1124
1127
  if not args.remote_skip_validation:
1125
1128
  if not args.quiet:
1126
- print(color.cyan("Validating remote resources..."), file=sys.stderr)
1129
+ print(color.cyan("🔍 Validating remote resources..."), file=sys.stderr)
1127
1130
 
1128
1131
  validation_result = await ssh_client.validate_remote_resources()
1129
1132
 
1130
1133
  if not validation_result.get("can_connect"):
1131
1134
  await ssh_client.disconnect()
1132
- print("Error: Cannot connect to remote server", file=sys.stderr)
1135
+ print(color.error(" Cannot connect to remote server"), file=sys.stderr)
1133
1136
  return 1
1134
1137
 
1135
1138
  if not validation_result.get("work_dir_writable"):
1136
1139
  await ssh_client.disconnect()
1137
- print(f"Error: Work directory not writable: {ssh_config.work_dir}", file=sys.stderr)
1140
+ print(color.error(f" Work directory not writable: {ssh_config.work_dir}"), file=sys.stderr)
1138
1141
  return 1
1139
1142
 
1140
1143
  if not validation_result.get("container_runtime_available"):
1141
1144
  await ssh_client.disconnect()
1142
1145
  runtime_str = ssh_config.container_runtime or "docker/podman"
1143
- print(f"Error: Container runtime not available: {runtime_str}", file=sys.stderr)
1146
+ print(color.error(f" Container runtime not available: {runtime_str}"), file=sys.stderr)
1144
1147
  return 1
1145
1148
 
1146
1149
  if not validation_result.get("disk_space_min_5gb"):
1147
- print(f"Warning: Less than 5GB available on remote", file=sys.stderr)
1150
+ print(color.warning(f" Less than 5GB available on remote"), file=sys.stderr)
1148
1151
  if not args.yes and sys.stdin.isatty():
1149
1152
  if not confirm_proceed("Continue anyway?"):
1150
1153
  await ssh_client.disconnect()
1151
1154
  return 130
1152
1155
 
1153
1156
  if not validation_result.get("memory_min_2gb"):
1154
- print(f"Warning: Less than 2GB available memory on remote", file=sys.stderr)
1157
+ print(color.warning(f" Less than 2GB available memory on remote"), file=sys.stderr)
1155
1158
  if not args.yes and sys.stdin.isatty():
1156
1159
  if not confirm_proceed("Continue anyway?"):
1157
1160
  await ssh_client.disconnect()
1158
1161
  return 130
1159
1162
 
1160
1163
  if not args.quiet:
1161
- print(color.green("✓ All remote resources validated"), file=sys.stderr)
1164
+ print(color.success("✓ All remote resources validated"), file=sys.stderr)
1162
1165
 
1163
1166
  # If --remote-validate-only, exit here
1164
1167
  if args.remote_validate_only:
1165
1168
  await ssh_client.disconnect()
1166
- print("Remote validation successful", file=sys.stderr)
1169
+ print(color.success("Remote validation successful"), file=sys.stderr)
1167
1170
  return 0
1168
1171
 
1169
1172
  # Phase 2.4.2: File upload, remote conversion, and download
@@ -1175,7 +1178,9 @@ def main_async_remote(args) -> int:
1175
1178
  print(f"Error: Input file or directory not found: {args.input}", file=sys.stderr)
1176
1179
  return 1
1177
1180
 
1178
- files_to_convert = get_files_to_convert(input_path.resolve(), args.glob, args.recursive)
1181
+ # Store resolved path as base for relative path calculations
1182
+ input_base = input_path.resolve()
1183
+ files_to_convert = get_files_to_convert(input_base, args.glob, args.recursive)
1179
1184
 
1180
1185
  if not files_to_convert:
1181
1186
  await ssh_client.disconnect()
@@ -1285,19 +1290,10 @@ def main_async_remote(args) -> int:
1285
1290
 
1286
1291
  # Determine output path
1287
1292
  output_dir = Path(args.out_dir)
1293
+ output_file = get_output_path(input_file, input_base, output_dir, args.flat)
1288
1294
 
1289
- # Preserve directory structure if not flat
1290
- if not args.flat and input_path.is_dir():
1291
- try:
1292
- rel_path = input_file.relative_to(input_path)
1293
- output_subdir = output_dir / rel_path.parent
1294
- except ValueError:
1295
- output_subdir = output_dir
1296
- else:
1297
- output_subdir = output_dir
1298
-
1299
- output_subdir.mkdir(parents=True, exist_ok=True)
1300
- output_file = output_subdir / f"{input_file.stem}.md"
1295
+ # Ensure output directory exists
1296
+ output_file.parent.mkdir(parents=True, exist_ok=True)
1301
1297
 
1302
1298
  # Check if output exists and skip if not overwrite
1303
1299
  if output_file.exists() and not args.overwrite:
@@ -1531,13 +1527,21 @@ def main_async_remote(args) -> int:
1531
1527
 
1532
1528
  # Print summary
1533
1529
  print(color.cyan(f"\n{'='*60}"), file=sys.stderr)
1534
- print(color.cyan(f"Remote conversion complete:"), file=sys.stderr)
1535
- print(color.green(f" Successful: {successful}"), file=sys.stderr)
1530
+ print(color.bold_cyan("📊 Remote Conversion Summary"), file=sys.stderr)
1531
+ print(color.cyan(f"{'='*60}"), file=sys.stderr)
1532
+
1533
+ # Format summary with emojis and colors
1534
+ total = len(files_to_convert)
1535
+ success_pct = f" ({successful}/{total})" if total > 0 else ""
1536
+ failed_pct = f" ({failed}/{total})" if total > 0 else ""
1537
+ skipped_pct = f" ({total - successful - failed}/{total})" if total > 0 else ""
1538
+
1539
+ print(f" {color.green('✓ Successful:')} {color.bold_green(str(successful))}{success_pct}", file=sys.stderr)
1536
1540
  if failed > 0:
1537
- print(color.yellow(f" Failed: {failed}"), file=sys.stderr)
1538
- else:
1539
- print(f" Failed: {failed}", file=sys.stderr)
1540
- print(f" Total: {len(files_to_convert)}", file=sys.stderr)
1541
+ print(f" {color.red('✗ Failed:')} {color.bold_red(str(failed))}{failed_pct}", file=sys.stderr)
1542
+ if (total - successful - failed) > 0:
1543
+ print(f" {color.yellow('⊘ Skipped:')} {color.bold_yellow(str(total - successful - failed))}{skipped_pct}", file=sys.stderr)
1544
+ print(f" {color.cyan('Total:')} {color.bold(str(total))}", file=sys.stderr)
1541
1545
  print(color.cyan(f"{'='*60}"), file=sys.stderr)
1542
1546
 
1543
1547
  return 0 if failed == 0 else 1
@@ -1585,7 +1589,9 @@ def main_async_remote(args) -> int:
1585
1589
 
1586
1590
  def main() -> int:
1587
1591
  """Main entry point for the CLI."""
1588
- print(f"mdify v{__version__}", file=sys.stderr)
1592
+ from mdify.formatting import Colorizer
1593
+ color = Colorizer(sys.stderr)
1594
+ print(color.bold_cyan(f"📄 mdify v{__version__}"), file=sys.stderr)
1589
1595
  args = parse_args()
1590
1596
 
1591
1597
  # Handle --check-update flag
@@ -1751,16 +1757,20 @@ def main() -> int:
1751
1757
  total_size = sum(f.stat().st_size for f in files_to_convert)
1752
1758
 
1753
1759
  if not args.quiet:
1754
- print(f"Found {total_files} file(s) to convert ({format_size(total_size)})")
1755
- print(f"Source: {input_path.resolve()}")
1756
- print(f"Output: {output_dir.resolve()}")
1757
- print(f"Using runtime: {runtime}")
1758
- print(f"Using image: {image}")
1760
+ from mdify.formatting import Colorizer
1761
+ color_info = Colorizer(sys.stdout)
1762
+ print(f"{color_info.bright_cyan('📦 Found')} {color_info.bold(str(total_files))} {color_info.bright_cyan('file(s)')} {color_info.dim_white(f'({format_size(total_size)})')}")
1763
+ print(f"{color_info.cyan('📁 Source:')} {color_info.bright_white(str(input_path.resolve()))}")
1764
+ print(f"{color_info.cyan('💾 Output:')} {color_info.bright_white(str(output_dir.resolve()))}")
1765
+ print(f"{color_info.cyan('🐳 Runtime:')} {color_info.bright_white(runtime)}")
1766
+ print(f"{color_info.cyan('🖼️ Image:')} {color_info.dim_white(image)}")
1759
1767
  print()
1760
1768
 
1761
1769
  if args.mask:
1770
+ from mdify.formatting import Colorizer
1771
+ color_warn = Colorizer(sys.stderr)
1762
1772
  print(
1763
- "Warning: --mask is not supported with docling-serve and will be ignored",
1773
+ color_warn.warning(" --mask is not supported with docling-serve and will be ignored"),
1764
1774
  file=sys.stderr,
1765
1775
  )
1766
1776
 
@@ -1777,7 +1787,9 @@ def main() -> int:
1777
1787
 
1778
1788
  try:
1779
1789
  if not args.quiet:
1780
- print(f"Starting docling-serve container...\n")
1790
+ from mdify.formatting import Colorizer
1791
+ color_start = Colorizer(sys.stdout)
1792
+ print(f"{color_start.bright_cyan('▶️ Starting')} {color_start.bright_white('docling-serve')} {color_start.bright_cyan('container')}...\n")
1781
1793
 
1782
1794
  # Apply resource profile
1783
1795
  profile = RESOURCE_PROFILES[args.profile]
@@ -2016,22 +2028,30 @@ def main() -> int:
2016
2028
 
2017
2029
  # Print summary
2018
2030
  if not args.quiet:
2031
+ from mdify.formatting import Colorizer
2032
+ color_out = Colorizer(sys.stdout)
2019
2033
  print()
2020
- print("=" * 50)
2021
- print("Conversion Summary:")
2022
- print(f" Total files: {total_files}")
2023
- print(f" Successful: {success_count}")
2024
- print(f" Skipped: {skipped_count}")
2025
- print(f" Failed: {failed_count}")
2026
- print(f" Total time: {format_duration(total_elapsed)}")
2027
- print("=" * 50)
2034
+ print(color_out.cyan("=" * 60))
2035
+ print(color_out.bold_cyan("📊 Local Conversion Summary"))
2036
+ print(color_out.cyan("=" * 60))
2037
+ print(f" {color_out.cyan('Total files:')} {color_out.bold(str(total_files))}")
2038
+ if success_count > 0:
2039
+ print(f" {color_out.green('✓ Successful:')} {color_out.bold_green(str(success_count))}")
2040
+ if skipped_count > 0:
2041
+ print(f" {color_out.yellow('⊘ Skipped:')} {color_out.bold_yellow(str(skipped_count))}")
2042
+ if failed_count > 0:
2043
+ print(f" {color_out.red('✗ Failed:')} {color_out.bold_red(str(failed_count))}")
2044
+ print(f" {color_out.cyan('Total time:')} {color_out.bright_cyan(format_duration(total_elapsed))}")
2045
+ print(color_out.cyan("=" * 60))
2028
2046
 
2029
2047
  except KeyboardInterrupt:
2030
2048
  if not args.quiet:
2031
- print("\n\nInterrupted by user. Container stopped.")
2049
+ from mdify.formatting import Colorizer
2050
+ color_out = Colorizer(sys.stdout)
2051
+ print(f"\n\n{color_out.warning('⚠ Interrupted by user. Container stopped.')}")
2032
2052
  if success_count > 0 or skipped_count > 0 or failed_count > 0:
2033
2053
  print(
2034
- f"Partial progress: {success_count} successful, {failed_count} failed, {skipped_count} skipped"
2054
+ f"{color_out.dim_white('Partial progress:')} {color_out.green(str(success_count))} successful, {color_out.red(str(failed_count))} failed, {color_out.yellow(str(skipped_count))} skipped"
2035
2055
  )
2036
2056
  return 130
2037
2057
 
@@ -0,0 +1,138 @@
1
+ """Formatting helpers for CLI output."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import TextIO
7
+
8
+
9
+ class Colorizer:
10
+ """ANSI color and style helper for terminal output."""
11
+
12
+ # ANSI color codes
13
+ RESET = "0"
14
+ BOLD = "1"
15
+ DIM = "2"
16
+ ITALIC = "3"
17
+ UNDERLINE = "4"
18
+
19
+ # Colors
20
+ BLACK = "30"
21
+ RED = "31"
22
+ GREEN = "32"
23
+ YELLOW = "33"
24
+ BLUE = "34"
25
+ MAGENTA = "35"
26
+ CYAN = "36"
27
+ WHITE = "37"
28
+ BRIGHT_BLACK = "90"
29
+ BRIGHT_RED = "91"
30
+ BRIGHT_GREEN = "92"
31
+ BRIGHT_YELLOW = "93"
32
+ BRIGHT_BLUE = "94"
33
+ BRIGHT_MAGENTA = "95"
34
+ BRIGHT_CYAN = "96"
35
+ BRIGHT_WHITE = "97"
36
+
37
+ def __init__(self, stream: TextIO) -> None:
38
+ force_color = os.environ.get("FORCE_COLOR")
39
+ no_color = os.environ.get("NO_COLOR")
40
+ self.enabled = bool(force_color) or (stream.isatty() and not no_color)
41
+
42
+ def _apply(self, text: str, *codes: str) -> str:
43
+ """Apply ANSI codes to text. Supports multiple codes."""
44
+ if not self.enabled:
45
+ return text
46
+ code_str = ";".join(codes)
47
+ return f"\033[{code_str}m{text}\033[0m"
48
+
49
+ def color(self, text: str, code: str) -> str:
50
+ """Apply a single color code."""
51
+ return self._apply(text, code)
52
+
53
+ # Basic colors
54
+ def green(self, text: str) -> str:
55
+ return self._apply(text, self.GREEN)
56
+
57
+ def yellow(self, text: str) -> str:
58
+ return self._apply(text, self.YELLOW)
59
+
60
+ def cyan(self, text: str) -> str:
61
+ return self._apply(text, self.CYAN)
62
+
63
+ def red(self, text: str) -> str:
64
+ return self._apply(text, self.RED)
65
+
66
+ def blue(self, text: str) -> str:
67
+ return self._apply(text, self.BLUE)
68
+
69
+ def magenta(self, text: str) -> str:
70
+ return self._apply(text, self.MAGENTA)
71
+
72
+ def white(self, text: str) -> str:
73
+ return self._apply(text, self.WHITE)
74
+
75
+ # Bright colors
76
+ def bright_green(self, text: str) -> str:
77
+ return self._apply(text, self.BRIGHT_GREEN)
78
+
79
+ def bright_yellow(self, text: str) -> str:
80
+ return self._apply(text, self.BRIGHT_YELLOW)
81
+
82
+ def bright_cyan(self, text: str) -> str:
83
+ return self._apply(text, self.BRIGHT_CYAN)
84
+
85
+ def bright_red(self, text: str) -> str:
86
+ return self._apply(text, self.BRIGHT_RED)
87
+
88
+ def bright_white(self, text: str) -> str:
89
+ return self._apply(text, self.BRIGHT_WHITE)
90
+
91
+ # Styles
92
+ def bold(self, text: str) -> str:
93
+ return self._apply(text, self.BOLD)
94
+
95
+ def dim(self, text: str) -> str:
96
+ return self._apply(text, self.DIM)
97
+
98
+ def underline(self, text: str) -> str:
99
+ return self._apply(text, self.UNDERLINE)
100
+
101
+ # Combined styles
102
+ def bold_green(self, text: str) -> str:
103
+ return self._apply(text, self.BOLD, self.GREEN)
104
+
105
+ def bold_cyan(self, text: str) -> str:
106
+ return self._apply(text, self.BOLD, self.CYAN)
107
+
108
+ def bold_yellow(self, text: str) -> str:
109
+ return self._apply(text, self.BOLD, self.YELLOW)
110
+
111
+ def bold_red(self, text: str) -> str:
112
+ return self._apply(text, self.BOLD, self.RED)
113
+
114
+ def dim_cyan(self, text: str) -> str:
115
+ return self._apply(text, self.DIM, self.CYAN)
116
+
117
+ def dim_white(self, text: str) -> str:
118
+ return self._apply(text, self.DIM, self.WHITE)
119
+
120
+ def success(self, text: str) -> str:
121
+ """Styled success message (bold green)."""
122
+ return self._apply(text, self.BOLD, self.GREEN)
123
+
124
+ def error(self, text: str) -> str:
125
+ """Styled error message (bold red)."""
126
+ return self._apply(text, self.BOLD, self.RED)
127
+
128
+ def warning(self, text: str) -> str:
129
+ """Styled warning message (bold yellow)."""
130
+ return self._apply(text, self.BOLD, self.YELLOW)
131
+
132
+ def info(self, text: str) -> str:
133
+ """Styled info message (cyan)."""
134
+ return self._apply(text, self.CYAN)
135
+
136
+ def muted(self, text: str) -> str:
137
+ """Muted text (dim white)."""
138
+ return self._apply(text, self.DIM, self.WHITE)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdify-cli
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Convert PDFs and document images into structured Markdown for LLM workflows
5
5
  Author: tiroq
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mdify-cli"
3
- version = "3.1.0"
3
+ version = "3.2.0"
4
4
  description = "Convert PDFs and document images into structured Markdown for LLM workflows"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,29 +0,0 @@
1
- """Formatting helpers for CLI output."""
2
-
3
- from __future__ import annotations
4
-
5
- import os
6
- from typing import TextIO
7
-
8
-
9
- class Colorizer:
10
- """ANSI color helper for terminal output."""
11
-
12
- def __init__(self, stream: TextIO) -> None:
13
- force_color = os.environ.get("FORCE_COLOR")
14
- no_color = os.environ.get("NO_COLOR")
15
- self.enabled = bool(force_color) or (stream.isatty() and not no_color)
16
-
17
- def color(self, text: str, code: str) -> str:
18
- if not self.enabled:
19
- return text
20
- return f"\033[{code}m{text}\033[0m"
21
-
22
- def green(self, text: str) -> str:
23
- return self.color(text, "32")
24
-
25
- def yellow(self, text: str) -> str:
26
- return self.color(text, "33")
27
-
28
- def cyan(self, text: str) -> str:
29
- return self.color(text, "36")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes