webscout 6.4__py3-none-any.whl → 6.6__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 webscout might be problematic. Click here for more details.

Files changed (116) hide show
  1. webscout/AIutel.py +7 -54
  2. webscout/DWEBS.py +48 -26
  3. webscout/{YTdownloader.py → Extra/YTToolkit/YTdownloader.py} +990 -1103
  4. webscout/Extra/YTToolkit/__init__.py +3 -0
  5. webscout/{transcriber.py → Extra/YTToolkit/transcriber.py} +1 -1
  6. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  7. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  8. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  9. webscout/Extra/YTToolkit/ytapi/extras.py +45 -0
  10. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  11. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  12. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  13. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  14. webscout/Extra/YTToolkit/ytapi/query.py +37 -0
  15. webscout/Extra/YTToolkit/ytapi/stream.py +60 -0
  16. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  17. webscout/Extra/YTToolkit/ytapi/video.py +102 -0
  18. webscout/Extra/__init__.py +2 -1
  19. webscout/Extra/autocoder/autocoder_utiles.py +119 -101
  20. webscout/Extra/autocoder/rawdog.py +679 -680
  21. webscout/Extra/gguf.py +441 -441
  22. webscout/Extra/markdownlite/__init__.py +862 -0
  23. webscout/Extra/weather_ascii.py +2 -2
  24. webscout/Provider/AISEARCH/__init__.py +2 -0
  25. webscout/Provider/AISEARCH/ooai.py +155 -0
  26. webscout/Provider/Amigo.py +70 -85
  27. webscout/Provider/{prefind.py → Jadve.py} +72 -70
  28. webscout/Provider/Netwrck.py +235 -0
  29. webscout/Provider/Openai.py +4 -3
  30. webscout/Provider/PI.py +292 -221
  31. webscout/Provider/PizzaGPT.py +3 -3
  32. webscout/Provider/Reka.py +0 -1
  33. webscout/Provider/TTS/__init__.py +5 -1
  34. webscout/Provider/TTS/deepgram.py +183 -0
  35. webscout/Provider/TTS/elevenlabs.py +137 -0
  36. webscout/Provider/TTS/gesserit.py +151 -0
  37. webscout/Provider/TTS/murfai.py +139 -0
  38. webscout/Provider/TTS/parler.py +134 -107
  39. webscout/Provider/TTS/streamElements.py +360 -275
  40. webscout/Provider/TTS/utils.py +280 -0
  41. webscout/Provider/TTS/voicepod.py +116 -116
  42. webscout/Provider/TeachAnything.py +15 -2
  43. webscout/Provider/Youchat.py +42 -8
  44. webscout/Provider/__init__.py +8 -21
  45. webscout/Provider/meta.py +794 -779
  46. webscout/Provider/multichat.py +230 -0
  47. webscout/Provider/promptrefine.py +2 -2
  48. webscout/Provider/talkai.py +10 -13
  49. webscout/Provider/turboseek.py +5 -4
  50. webscout/Provider/tutorai.py +8 -112
  51. webscout/Provider/typegpt.py +5 -7
  52. webscout/Provider/x0gpt.py +81 -9
  53. webscout/Provider/yep.py +123 -361
  54. webscout/__init__.py +33 -28
  55. webscout/conversation.py +24 -9
  56. webscout/exceptions.py +188 -20
  57. webscout/litprinter/__init__.py +719 -831
  58. webscout/litprinter/colors.py +54 -0
  59. webscout/optimizers.py +420 -270
  60. webscout/prompt_manager.py +279 -279
  61. webscout/scout/__init__.py +8 -0
  62. webscout/scout/core/__init__.py +7 -0
  63. webscout/scout/core/crawler.py +140 -0
  64. webscout/scout/core/scout.py +571 -0
  65. webscout/scout/core/search_result.py +96 -0
  66. webscout/scout/core/text_analyzer.py +63 -0
  67. webscout/scout/core/text_utils.py +277 -0
  68. webscout/scout/core/web_analyzer.py +52 -0
  69. webscout/scout/core.py +884 -0
  70. webscout/scout/element.py +460 -0
  71. webscout/scout/parsers/__init__.py +69 -0
  72. webscout/scout/parsers/html5lib_parser.py +172 -0
  73. webscout/scout/parsers/html_parser.py +236 -0
  74. webscout/scout/parsers/lxml_parser.py +178 -0
  75. webscout/scout/utils.py +38 -0
  76. webscout/update_checker.py +184 -125
  77. webscout/version.py +1 -1
  78. webscout/zeroart/__init__.py +55 -0
  79. webscout/zeroart/base.py +60 -0
  80. webscout/zeroart/effects.py +99 -0
  81. webscout/zeroart/fonts.py +816 -0
  82. webscout/zerodir/__init__.py +225 -0
  83. {webscout-6.4.dist-info → webscout-6.6.dist-info}/METADATA +18 -231
  84. webscout-6.6.dist-info/RECORD +197 -0
  85. webscout-6.6.dist-info/top_level.txt +2 -0
  86. webstoken/__init__.py +30 -0
  87. webstoken/classifier.py +189 -0
  88. webstoken/keywords.py +216 -0
  89. webstoken/language.py +128 -0
  90. webstoken/ner.py +164 -0
  91. webstoken/normalizer.py +35 -0
  92. webstoken/processor.py +77 -0
  93. webstoken/sentiment.py +206 -0
  94. webstoken/stemmer.py +73 -0
  95. webstoken/t.py +75 -0
  96. webstoken/tagger.py +60 -0
  97. webstoken/tokenizer.py +158 -0
  98. webscout/Agents/Onlinesearcher.py +0 -182
  99. webscout/Agents/__init__.py +0 -2
  100. webscout/Agents/functioncall.py +0 -248
  101. webscout/Bing_search.py +0 -251
  102. webscout/Provider/Perplexity.py +0 -599
  103. webscout/Provider/RoboCoders.py +0 -206
  104. webscout/Provider/genspark.py +0 -225
  105. webscout/Provider/perplexitylabs.py +0 -265
  106. webscout/Provider/twitterclone.py +0 -251
  107. webscout/Provider/upstage.py +0 -230
  108. webscout/gpt4free.py +0 -666
  109. webscout/requestsHTMLfix.py +0 -775
  110. webscout/webai.py +0 -2590
  111. webscout-6.4.dist-info/RECORD +0 -154
  112. webscout-6.4.dist-info/top_level.txt +0 -1
  113. /webscout/Provider/{felo_search.py → AISEARCH/felo_search.py} +0 -0
  114. {webscout-6.4.dist-info → webscout-6.6.dist-info}/LICENSE.md +0 -0
  115. {webscout-6.4.dist-info → webscout-6.6.dist-info}/WHEEL +0 -0
  116. {webscout-6.4.dist-info → webscout-6.6.dist-info}/entry_points.txt +0 -0
@@ -1,831 +1,719 @@
1
- """
2
- Welcome to WebScout's Advanced Console Printer!
3
-
4
- About This Printer:
5
- - A powerful, feature-rich console output manager
6
- - Created by Abhay Koul as part of the WebScout project
7
- - Designed for beautiful, informative, and interactive terminal output
8
-
9
- Core Features:
10
- >>> printer.success("Operation completed successfully! ")
11
- >>> printer.warning("Important notice! ")
12
- >>> printer.info("Here's something interesting ")
13
- >>> printer.error("Houston, we have a problem! ")
14
-
15
- Special Features:
16
- - Rich ANSI color support
17
- - Data visualization tools
18
- - Progress bars and spinners
19
- - Code syntax highlighting
20
- - Markdown rendering
21
- - JSON pretty printing
22
-
23
- For the best experience, use a terminal that supports UTF-8 and ANSI colors.
24
-
25
- >>> # Basic Printing with Style
26
- >>> printer.print("Hello World!", color="blue", bold=True)
27
- >>> printer.print("Important Notice", bg_color="yellow", italic=True)
28
-
29
- >>> # Layout and Formatting
30
- >>> printer.print("Centered Text", center=True, width=50)
31
- >>> printer.print("Indented Text", indent=4, prefix="→ ")
32
-
33
- >>> # Borders and Decorations
34
- >>> printer.print("Special Message", border=True, rounded_corners=True)
35
- >>> printer.print("Alert", border=True, border_color="red", double_border=True)
36
-
37
- >>> # Animations
38
- >>> printer.print("Loading...", animate=True, animation_type="typing")
39
- >>> printer.print("Processing", animate=True, animation_speed=0.1)
40
-
41
- >>> # Data Visualization
42
- >>> data = {"status": "active", "users": 100}
43
- >>> printer.print(data, as_json=True) # Pretty JSON
44
- >>> printer.print(data, as_tree=True) # Tree View
45
-
46
- >>> # Code Highlighting
47
- >>> code = '''def greet(): print("Hello!")'''
48
- >>> printer.print(code, as_code=True, language="python")
49
-
50
- >>> # Tables
51
- >>> headers = ["Name", "Status"]
52
- >>> rows = [["Server", "Online"]]
53
- >>> printer.print([headers, rows], as_table=True)
54
-
55
- >>> # Markdown Support
56
- >>> printer.print("# Title\n- List item", markdown=True)
57
- """
58
-
59
- import sys
60
- import os
61
- import json
62
- import time
63
- import threading
64
- import re
65
- from typing import Any, Optional, TextIO, Union, Sequence, Dict, List
66
- from datetime import datetime
67
- import textwrap
68
- from collections import defaultdict
69
- import shutil
70
- import inspect
71
- try:
72
- from pygments import highlight
73
- from pygments.lexers import get_lexer_by_name
74
- from pygments.formatters import Terminal256Formatter
75
- PYGMENTS_AVAILABLE = True
76
- except ImportError:
77
- PYGMENTS_AVAILABLE = False
78
-
79
- # Enable UTF-8 output on Windows
80
- if sys.platform == 'win32':
81
- import ctypes
82
- kernel32 = ctypes.windll.kernel32
83
- kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
84
- sys.stdout.reconfigure(encoding='utf-8')
85
-
86
- # Enable ANSI escape sequences for Windows
87
- if os.name == 'nt':
88
- import ctypes
89
- kernel32 = ctypes.windll.kernel32
90
- kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
91
-
92
- # ANSI Color Codes
93
- class Colors:
94
- """ANSI color codes for terminal output."""
95
- # Base colors
96
- BLACK = '\033[30m'
97
- RED = '\033[31m'
98
- GREEN = '\033[32m'
99
- YELLOW = '\033[33m'
100
- BLUE = '\033[34m'
101
- MAGENTA = '\033[35m'
102
- CYAN = '\033[36m'
103
- WHITE = '\033[37m'
104
- GRAY = '\033[90m'
105
-
106
- # Bright colors
107
- BRIGHT_BLACK = '\033[90m'
108
- BRIGHT_RED = '\033[91m'
109
- BRIGHT_GREEN = '\033[92m'
110
- BRIGHT_YELLOW = '\033[93m'
111
- BRIGHT_BLUE = '\033[94m'
112
- BRIGHT_MAGENTA = '\033[95m'
113
- BRIGHT_CYAN = '\033[96m'
114
- BRIGHT_WHITE = '\033[97m'
115
-
116
- # Background colors
117
- BG_BLACK = '\033[40m'
118
- BG_RED = '\033[41m'
119
- BG_GREEN = '\033[42m'
120
- BG_YELLOW = '\033[43m'
121
- BG_BLUE = '\033[44m'
122
- BG_MAGENTA = '\033[45m'
123
- BG_CYAN = '\033[46m'
124
- BG_WHITE = '\033[47m'
125
-
126
- # Styles
127
- BOLD = '\033[1m'
128
- DIM = '\033[2m'
129
- ITALIC = '\033[3m'
130
- UNDERLINE = '\033[4m'
131
- BLINK = '\033[5m'
132
- REVERSE = '\033[7m'
133
- STRIKE = '\033[9m'
134
- HIDDEN = '\033[8m'
135
-
136
- # Special
137
- RESET = '\033[0m'
138
- CLEAR_SCREEN = '\033[2J'
139
- CLEAR_LINE = '\033[2K'
140
-
141
- # Cursor movement
142
- UP = '\033[1A'
143
- DOWN = '\033[1B'
144
- RIGHT = '\033[1C'
145
- LEFT = '\033[1D'
146
-
147
- class SyntaxTheme:
148
- """Syntax highlighting theme."""
149
- KEYWORD = Colors.MAGENTA + Colors.BOLD
150
- STRING = Colors.GREEN
151
- NUMBER = Colors.CYAN
152
- COMMENT = Colors.BRIGHT_BLACK + Colors.ITALIC
153
- FUNCTION = Colors.BRIGHT_BLUE
154
- CLASS = Colors.BRIGHT_YELLOW + Colors.BOLD
155
- OPERATOR = Colors.WHITE
156
- BRACKET = Colors.WHITE
157
- VARIABLE = Colors.BRIGHT_WHITE
158
-
159
- class MarkdownTheme:
160
- """Theme for markdown elements."""
161
- H1 = Colors.BOLD + Colors.BLUE
162
- H2 = Colors.BOLD + Colors.CYAN
163
- H3 = Colors.BOLD + Colors.GREEN
164
- BOLD = Colors.BOLD
165
- ITALIC = Colors.ITALIC
166
- CODE = Colors.YELLOW
167
- LINK = Colors.BLUE + Colors.UNDERLINE
168
- LIST_BULLET = Colors.CYAN + "•" + Colors.RESET
169
- QUOTE = Colors.GRAY
170
- STRIKE = Colors.STRIKE
171
- TABLE = Colors.GREEN
172
- TASK = Colors.YELLOW
173
- DETAILS = Colors.MAGENTA
174
-
175
- class ThemeStyles:
176
- SUCCESS = f"{Colors.GREEN}{Colors.BOLD}"
177
- ERROR = f"{Colors.RED}{Colors.BOLD}"
178
- WARNING = f"{Colors.YELLOW}{Colors.BOLD}"
179
- INFO = f"{Colors.BLUE}{Colors.BOLD}"
180
- DEBUG = f"{Colors.MAGENTA}"
181
- CODE = f"{Colors.CYAN}"
182
-
183
- class HoverInfo:
184
- """Hover information for different elements."""
185
- BANNER = "A fancy banner to make your output pop!"
186
- SUCCESS = "Something went right! "
187
- ERROR = "Oops! Something went wrong "
188
- WARNING = "Heads up! Something needs attention "
189
- INFO = "Just some helpful info "
190
- TABLE = "Data organized in rows and columns "
191
- TREE = "Hierarchical data visualization "
192
- JSON = "Pretty-printed JSON data "
193
- CODE = "Syntax-highlighted code block "
194
-
195
- class ProgressBar:
196
- def __init__(self, total: int, width: int = 40, prefix: str = '', suffix: str = ''):
197
- self.total = total
198
- self.width = width
199
- self.prefix = prefix
200
- self.suffix = suffix
201
- self.current = 0
202
-
203
- def update(self, current: int):
204
- self.current = current
205
- filled = int(self.width * current / self.total)
206
- bar = f"{Colors.GREEN}{'█' * filled}{Colors.RESET}{'░' * (self.width - filled)}"
207
- percent = f"{Colors.CYAN}{int(100 * current / self.total)}%{Colors.RESET}"
208
- print(f'\r{self.prefix} |{bar}| {percent} {self.suffix}', end='', flush=True)
209
- if current >= self.total:
210
- print()
211
-
212
- class Spinner:
213
- def __init__(self, message: str = ''):
214
- self.message = message
215
- self.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
216
- self.running = False
217
- self.thread = None
218
-
219
- def spin(self):
220
- while self.running:
221
- for frame in self.frames:
222
- if not self.running:
223
- break
224
- print(f'\r{Colors.CYAN}{frame}{Colors.RESET} {self.message}', end='', flush=True)
225
- time.sleep(0.1)
226
-
227
- def __enter__(self):
228
- self.start()
229
- return self
230
-
231
- def __exit__(self, exc_type, exc_val, exc_tb):
232
- self.stop()
233
-
234
- def start(self):
235
- self.running = True
236
- self.thread = threading.Thread(target=self.spin)
237
- self.thread.start()
238
-
239
- def stop(self):
240
- self.running = False
241
- if self.thread:
242
- self.thread.join()
243
- print('\r' + ' ' * (len(self.message) + 2), end='', flush=True)
244
- print('\r', end='')
245
-
246
- class LitPrinter:
247
- def __init__(self,
248
- file: TextIO = sys.stdout,
249
- theme: Optional[dict] = None,
250
- indent_size: int = 4,
251
- buffer_size: int = 1024,
252
- syntax_theme: Optional[Dict[str, str]] = None,
253
- markdown_theme: Optional[Dict[str, str]] = None):
254
- self.file = file
255
- self.indent_size = indent_size
256
- self.buffer_size = buffer_size
257
- self._terminal_width = shutil.get_terminal_size().columns
258
- self._last_line = ""
259
- self._spinner_active = False
260
- self._progress_active = False
261
-
262
- # Default theme
263
- self.theme = theme or {
264
- "str": Colors.WHITE,
265
- "int": Colors.CYAN,
266
- "float": Colors.CYAN,
267
- "bool": Colors.YELLOW,
268
- "list": Colors.MAGENTA,
269
- "dict": Colors.BLUE,
270
- "none": Colors.RED,
271
- "timestamp": Colors.GREEN,
272
- "key": Colors.YELLOW,
273
- "bracket": Colors.BLUE,
274
- "comma": Colors.WHITE,
275
- "colon": Colors.WHITE,
276
- "url": f"{Colors.BLUE}{Colors.UNDERLINE}",
277
- "number": Colors.CYAN,
278
- "special": Colors.MAGENTA + Colors.BOLD,
279
- "error": Colors.RED + Colors.BOLD,
280
- "warning": Colors.YELLOW + Colors.BOLD,
281
- "success": Colors.GREEN + Colors.BOLD,
282
- "info": Colors.BLUE + Colors.BOLD,
283
- }
284
-
285
- self.syntax_theme = syntax_theme or vars(SyntaxTheme)
286
- self.markdown_theme = markdown_theme or vars(MarkdownTheme)
287
-
288
- def _get_terminal_width(self) -> int:
289
- """Get the terminal width or default to 80."""
290
- try:
291
- width = shutil.get_terminal_size().columns
292
- return width if width > 0 else 80
293
- except:
294
- return 80
295
-
296
- def _format_dict(self, d: dict, indent_level: int = 0) -> str:
297
- """Format dictionary with proper indentation and colors."""
298
- if not d:
299
- return f"{self.theme['bracket']}{{}}{Colors.RESET}"
300
-
301
- indent = " " * (self.indent_size * indent_level)
302
- next_indent = " " * (self.indent_size * (indent_level + 1))
303
-
304
- lines = [f"{self.theme['bracket']}{{{Colors.RESET}"]
305
-
306
- for i, (key, value) in enumerate(d.items()):
307
- # Format key with quotes if it's a string
308
- if isinstance(key, str):
309
- formatted_key = f"{self.theme['key']}'{key}'{Colors.RESET}"
310
- else:
311
- formatted_key = f"{self.theme['key']}{key}{Colors.RESET}"
312
-
313
- # Format value based on type
314
- if isinstance(value, dict):
315
- formatted_value = self._format_dict(value, indent_level + 1)
316
- elif isinstance(value, str):
317
- # Special handling for URLs
318
- if any(url_prefix in value.lower() for url_prefix in ['http://', 'https://', 'www.']):
319
- formatted_value = f"{self.theme['url']}{value}{Colors.RESET}"
320
- else:
321
- # Word wrap long strings
322
- if len(value) > 80:
323
- wrapped = textwrap.fill(value, width=80, subsequent_indent=next_indent + " ")
324
- formatted_value = f"{self.theme['str']}'{wrapped}'{Colors.RESET}"
325
- else:
326
- formatted_value = f"{self.theme['str']}'{value}'{Colors.RESET}"
327
- elif isinstance(value, (int, float)):
328
- formatted_value = f"{self.theme['number']}{value}{Colors.RESET}"
329
- elif isinstance(value, bool):
330
- formatted_value = f"{self.theme['bool']}{value}{Colors.RESET}"
331
- elif value is None:
332
- formatted_value = f"{self.theme['none']}None{Colors.RESET}"
333
- else:
334
- formatted_value = self._format_value(value)
335
-
336
- # Add comma if not last item
337
- comma = f"{self.theme['comma']},{Colors.RESET}" if i < len(d) - 1 else ""
338
-
339
- lines.append(f"{next_indent}{formatted_key}{self.theme['colon']}: {Colors.RESET}{formatted_value}{comma}")
340
-
341
- lines.append(f"{indent}{self.theme['bracket']}}}{Colors.RESET}")
342
- return '\n'.join(lines)
343
-
344
- def _format_sequence(self, seq: Sequence, indent_level: int = 0) -> str:
345
- """Format sequences (lists, tuples, sets) with proper indentation."""
346
- if not seq:
347
- return f"{self.theme['bracket']}[]{Colors.RESET}"
348
-
349
- indent = " " * (self.indent_size * indent_level)
350
- next_indent = " " * (self.indent_size * (indent_level + 1))
351
-
352
- lines = [f"{self.theme['bracket']}[{Colors.RESET}"]
353
-
354
- for i, item in enumerate(seq):
355
- formatted_item = self._format_value(item, indent_level + 1)
356
- comma = f"{self.theme['comma']},{Colors.RESET}" if i < len(seq) - 1 else ""
357
- lines.append(f"{next_indent}{formatted_item}{comma}")
358
-
359
- lines.append(f"{indent}{self.theme['bracket']}]{Colors.RESET}")
360
- return '\n'.join(lines)
361
-
362
- def _format_value(self, value: Any, indent_level: int = 0) -> str:
363
- """Enhanced format for any value with proper indentation and styling."""
364
- if value is None:
365
- return f"{self.theme['none']}None{Colors.RESET}"
366
-
367
- if isinstance(value, dict):
368
- return self._format_dict(value, indent_level)
369
-
370
- if isinstance(value, (list, tuple, set)):
371
- return self._format_sequence(value, indent_level)
372
-
373
- if isinstance(value, str):
374
- return str(value)
375
-
376
- if isinstance(value, bool):
377
- return f"{self.theme['bool']}{str(value)}{Colors.RESET}"
378
-
379
- if isinstance(value, (int, float)):
380
- return f"{self.theme['number']}{str(value)}{Colors.RESET}"
381
-
382
- if hasattr(value, '__dict__'):
383
- return self._format_dict(value.__dict__, indent_level)
384
-
385
- return str(value)
386
-
387
- def _highlight_code(self, code: str, language: str = "python") -> str:
388
- """Print code with that extra drip """
389
- if PYGMENTS_AVAILABLE:
390
- try:
391
- lexer = get_lexer_by_name(language)
392
- formatter = Terminal256Formatter(style='monokai')
393
- return highlight(code, lexer, formatter)
394
- except:
395
- pass
396
-
397
- if language != 'python':
398
- return code
399
-
400
- lines = []
401
- for line in code.split('\n'):
402
- if '#' in line:
403
- code_part, comment_part = line.split('#', 1)
404
- line = code_part + f"{self.syntax_theme['COMMENT']}#{comment_part}{Colors.RESET}"
405
-
406
- line = re.sub(r'(".*?"|\'.*?\')',
407
- f"{self.syntax_theme['STRING']}\\1{Colors.RESET}", line)
408
-
409
- line = re.sub(r'\b(\d+)\b',
410
- f"{self.syntax_theme['NUMBER']}\\1{Colors.RESET}", line)
411
-
412
- keywords = ['def', 'class', 'if', 'else', 'elif', 'for', 'while', 'try',
413
- 'except', 'finally', 'with', 'as', 'import', 'from', 'return']
414
-
415
- for keyword in keywords:
416
- line = re.sub(f'\\b{keyword}\\b',
417
- f"{self.syntax_theme['KEYWORD']}{keyword}{Colors.RESET}", line)
418
-
419
- lines.append(line)
420
-
421
- return '\n'.join(lines)
422
-
423
- def _format_markdown_stream(self, text: str) -> str:
424
- """Enhanced markdown formatting for streaming mode."""
425
- # Headers with emoji flair
426
- text = re.sub(r'^# (.+)$', f"{self.markdown_theme['H1']}🔥 \\1{Colors.RESET}", text, flags=re.M)
427
- text = re.sub(r'^## (.+)$', f"{self.markdown_theme['H2']}✨ \\1{Colors.RESET}", text, flags=re.M)
428
- text = re.sub(r'^### (.+)$', f"{self.markdown_theme['H3']}💫 \\1{Colors.RESET}", text, flags=re.M)
429
-
430
- # Bold, italic, and combined with multiple styles
431
- text = re.sub(r'\*\*\*(.+?)\*\*\*', f"{Colors.BOLD}{Colors.ITALIC}\\1{Colors.RESET}", text)
432
- text = re.sub(r'\*\*(.+?)\*\*', f"{Colors.BOLD}\\1{Colors.RESET}", text)
433
- text = re.sub(r'\*(.+?)\*', f"{Colors.ITALIC}\\1{Colors.RESET}", text)
434
- text = re.sub(r'__(.+?)__', f"{Colors.BOLD}\\1{Colors.RESET}", text)
435
- text = re.sub(r'_(.+?)_', f"{Colors.ITALIC}\\1{Colors.RESET}", text)
436
-
437
- # Code blocks and inline code
438
- text = re.sub(r'```(\w+)?\n(.*?)\n```', lambda m: self._highlight_code(m.group(2), m.group(1) or 'text'), text, flags=re.S)
439
- text = re.sub(r'`(.+?)`', f"{Colors.CYAN}\\1{Colors.RESET}", text)
440
-
441
- # Lists with proper indentation and bullets
442
- lines = text.split('\n')
443
- formatted_lines = []
444
- for i, line in enumerate(lines):
445
- # Match different bullet point styles
446
- bullet_match = re.match(r'^(\s*)([-•*]|\d+\.) (.+)$', line)
447
- if bullet_match:
448
- indent, bullet, content = bullet_match.groups()
449
- indent_level = len(indent) // 2
450
-
451
- # Choose bullet style based on nesting level
452
- if indent_level == 0:
453
- bullet_style = "•"
454
- elif indent_level == 1:
455
- bullet_style = "◦"
456
- else:
457
- bullet_style = "▪"
458
-
459
- # Format the line with proper indentation and bullet
460
- formatted_line = f"{' ' * (indent_level * 2)}{Colors.CYAN}{bullet_style}{Colors.RESET} {content}"
461
- formatted_lines.append(formatted_line)
462
- else:
463
- formatted_lines.append(line)
464
- text = '\n'.join(formatted_lines)
465
-
466
- # Links with underline
467
- text = re.sub(r'\[(.+?)\]\((.+?)\)', f"{Colors.BLUE}{Colors.UNDERLINE}\\1{Colors.RESET}", text)
468
-
469
- # Blockquotes with style
470
- text = re.sub(r'^> (.+)$', f"{self.markdown_theme['QUOTE']}│ \\1{Colors.RESET}", text, flags=re.M)
471
-
472
- # Strikethrough
473
- text = re.sub(r'~~(.+?)~~', f"{Colors.STRIKE}\\1{Colors.RESET}", text)
474
-
475
- # Task lists with fancy checkboxes
476
- text = re.sub(r'- \[ \] (.+)$', f"{self.markdown_theme['TASK']}☐ \\1{Colors.RESET}", text, flags=re.M)
477
- text = re.sub(r'- \[x\] (.+)$', f"{self.markdown_theme['TASK']}☑ \\1{Colors.RESET}", text, flags=re.M)
478
-
479
- # Tables with borders
480
- table_pattern = r'\|(.+?)\|[\r\n]+\|[-:| ]+\|[\r\n]+((?:\|.+?\|[\r\n]+)+)'
481
- text = re.sub(table_pattern, self._format_table_markdown, text, flags=re.M)
482
-
483
- return text
484
-
485
- def _format_table_markdown(self, match) -> str:
486
- """Format markdown tables with style."""
487
- header = [cell.strip() for cell in match.group(1).split('|') if cell.strip()]
488
- rows = []
489
- for row in match.group(2).strip().split('\n'):
490
- cells = [cell.strip() for cell in row.split('|')[1:-1]]
491
- if cells:
492
- rows.append(cells)
493
-
494
- # Get column widths
495
- widths = [max(len(str(row[i])) for row in [header] + rows) for i in range(len(header))]
496
-
497
- # Build table
498
- result = []
499
- # Header
500
- result.append('┌' + '┬'.join('─' * (w + 2) for w in widths) + '┐')
501
- result.append('│ ' + ' │ '.join(f"{h:<{w}}" for h, w in zip(header, widths)) + ' │')
502
- result.append('├' + '┼'.join('─' * (w + 2) for w in widths) + '┤')
503
- # Rows
504
- for row in rows:
505
- result.append('│ ' + ' │ '.join(f"{str(c):<{w}}" for c, w in zip(row, widths)) + ' │')
506
- result.append('└' + '┴'.join('─' * (w + 2) for w in widths) + '┘')
507
-
508
- return '\n'.join(result)
509
-
510
- def print(self, *args,
511
- # Builtin print compatibility
512
- sep: str = " ",
513
- end: str = "\n",
514
- file: Optional[TextIO] = None,
515
- flush: bool = True,
516
-
517
- # Styling options
518
- style: Optional[str] = None,
519
- color: Optional[str] = None,
520
- bg_color: Optional[str] = None,
521
- bold: bool = False,
522
- italic: bool = False,
523
- underline: bool = False,
524
- blink: bool = False,
525
- strike: bool = False,
526
- dim: bool = False,
527
- reverse: bool = False,
528
-
529
- # Layout options
530
- markdown: Optional[bool] = None,
531
- highlight: bool = False,
532
- center: bool = False,
533
- indent: int = 0,
534
- prefix: Optional[str] = None,
535
- suffix: Optional[str] = None,
536
- width: Optional[int] = None,
537
- padding: int = 0,
538
- margin: int = 0,
539
- align: str = "left",
540
-
541
- # Border options
542
- border: bool = False,
543
- border_style: Optional[str] = None,
544
- border_char: str = "─",
545
- border_color: Optional[str] = None,
546
- rounded_corners: bool = False,
547
- double_border: bool = False,
548
-
549
- # Animation options
550
- animate: bool = False,
551
- animation_speed: float = 0.05,
552
- animation_type: str = "typing",
553
-
554
- # Special features
555
- as_table: bool = False,
556
- as_tree: bool = False,
557
- as_json: bool = False,
558
- as_code: bool = False,
559
- language: str = "python",
560
-
561
- # Advanced features
562
- raw: bool = False) -> None:
563
- """
564
- Enhanced print with all builtin features plus rich formatting.
565
-
566
- Supports all builtin print parameters plus rich formatting features.
567
- Automatically detects and formats markdown content unless explicitly disabled.
568
- """
569
- # Handle raw output mode
570
- if raw:
571
- print(*args, sep=sep, end=end, file=file or self.file, flush=flush)
572
- return
573
-
574
- # Join args with separator
575
- output = sep.join(str(arg) for arg in args)
576
-
577
- # Auto-detect markdown if not explicitly set
578
- if markdown is None:
579
- markdown = any(marker in output for marker in [
580
- '#', '*', '_', '`', '>', '-', '•', '|',
581
- # '✨', '🔥', '💫', '☐', '☑',
582
- 'http://', 'https://',
583
- '```', '~~~',
584
- '<details>', '<summary>',
585
- ])
586
-
587
- # Apply markdown formatting if enabled
588
- if markdown:
589
- output = self._format_markdown_stream(output)
590
-
591
- # Build style string
592
- style_str = style or ""
593
- if color:
594
- style_str += getattr(Colors, color.upper(), "")
595
- if bg_color:
596
- style_str += getattr(Colors, f"BG_{bg_color.upper()}", "")
597
- if bold:
598
- style_str += Colors.BOLD
599
- if italic:
600
- style_str += Colors.ITALIC
601
- if underline:
602
- style_str += Colors.UNDERLINE
603
- if blink:
604
- style_str += Colors.BLINK
605
- if strike:
606
- style_str += Colors.STRIKE
607
- if dim:
608
- style_str += Colors.DIM
609
- if reverse:
610
- style_str += Colors.REVERSE
611
-
612
- # Apply style if any
613
- if style_str:
614
- output = f"{style_str}{output}{Colors.RESET}"
615
-
616
- # Handle special formatting
617
- if as_json:
618
- output = self._format_json(output)
619
- elif as_code:
620
- output = self._highlight_code(output, language)
621
- elif as_table:
622
- if isinstance(output, (list, tuple)):
623
- self.table(*output)
624
- return
625
- elif as_tree:
626
- if isinstance(output, (dict, list)):
627
- self.tree(output)
628
- return
629
-
630
- # Apply layout options
631
- if center:
632
- term_width = self._get_terminal_width()
633
- output = output.center(term_width)
634
- if indent > 0:
635
- output = " " * (indent * 4) + output
636
- if prefix:
637
- output = prefix + output
638
- if suffix:
639
- output = output + suffix
640
- if width:
641
- output = textwrap.fill(output, width=width)
642
-
643
- # Add borders
644
- if border:
645
- width = max(len(line) for line in output.split('\n'))
646
- border_top = '' + border_char * width + '┐'
647
- border_bottom = '' + border_char * width + '┘'
648
- output = f"{border_top}\n{output}\n{border_bottom}"
649
-
650
- # Handle animation
651
- if animate:
652
- for char in output:
653
- print(char, end="", flush=True)
654
- time.sleep(animation_speed)
655
- print(end=end, flush=flush)
656
- return
657
-
658
- # Final output
659
- print(output, end=end, file=file or self.file, flush=flush)
660
-
661
- def status(self, text: str, style: Optional[str] = None):
662
- """Print a status message that can be overwritten."""
663
- style = style or self.theme['info']
664
- self._clear_line()
665
- self._last_line = f"{style}{text}{Colors.RESET}"
666
- print(self._last_line, end='\r', file=self.file, flush=True)
667
-
668
- def banner(self, text: str, style: Optional[str] = None):
669
- """Print a fancy banner with hover info."""
670
- print(f"\033]1337;Custom=id=banner:{HoverInfo.BANNER}\a", end='')
671
- style = style or self.theme['special']
672
- width = self._get_terminal_width() - 4
673
-
674
- lines = textwrap.wrap(text, width=width, break_long_words=False)
675
-
676
- print('╔' + '═' * width + '╗')
677
- for line in lines:
678
- padding = width - len(line)
679
- print('║ ' + line + ' ' * padding + ' ')
680
- print('╚' + '═' * width + '╝')
681
- print("\033]1337;Custom=id=banner:end\a", end='')
682
-
683
- def success(self, text: str):
684
- """Print a success message with hover info."""
685
- print(f"\033]1337;Custom=id=success:{HoverInfo.SUCCESS}\a", end='')
686
- self.print(f"✓ {text}", style=self.theme['success'])
687
- print("\033]1337;Custom=id=success:end\a", end='')
688
-
689
- def error(self, text: str):
690
- """Print an error message with hover info."""
691
- print(f"\033]1337;Custom=id=error:{HoverInfo.ERROR}\a", end='')
692
- self.print(f"✗ {text}", style=self.theme['error'])
693
- print("\033]1337;Custom=id=error:end\a", end='')
694
-
695
- def warning(self, text: str):
696
- """Print a warning message with hover info."""
697
- print(f"\033]1337;Custom=id=warning:{HoverInfo.WARNING}\a", end='')
698
- self.print(f"⚠ {text}", style=self.theme['warning'])
699
- print("\033]1337;Custom=id=warning:end\a", end='')
700
-
701
- def info(self, text: str):
702
- """Print an info message with hover info."""
703
- print(f"\033]1337;Custom=id=info:{HoverInfo.INFO}\a", end='')
704
- self.print(f"ℹ {text}", style=self.theme['info'])
705
- print("\033]1337;Custom=id=info:end\a", end='')
706
-
707
- def table(self, headers: List[str], rows: List[List[Any]], style: Optional[str] = None):
708
- """Print a formatted table."""
709
- style = style or self.theme['special']
710
-
711
- col_widths = [len(h) for h in headers]
712
- for row in rows:
713
- for i, cell in enumerate(row):
714
- col_widths[i] = max(col_widths[i], len(str(cell)))
715
-
716
- header_line = '| ' + ' | '.join(f"{h:<{w}}" for h, w in zip(headers, col_widths)) + ' |'
717
- print(header_line)
718
-
719
- separator = '+' + '+'.join('-' * (w + 2) for w in col_widths) + '+'
720
- print(separator)
721
-
722
- for row in rows:
723
- row_line = '| ' + ' | '.join(f"{str(cell):<{w}}" for cell, w in zip(row, col_widths)) + ' |'
724
- print(row_line)
725
-
726
- def tree(self, data: Union[Dict, List], indent: int = 0):
727
- """Print a tree structure of nested data."""
728
- if isinstance(data, dict):
729
- for key, value in data.items():
730
- self.print(" " * indent + "├─ " + str(key) + ":", style=self.theme['key'])
731
- if isinstance(value, (dict, list)):
732
- self.tree(value, indent + 1)
733
- else:
734
- self.print(" " * (indent + 1) + "└─ " + str(value))
735
- elif isinstance(data, list):
736
- for item in data:
737
- if isinstance(item, (dict, list)):
738
- self.tree(item, indent + 1)
739
- else:
740
- self.print(" " * indent + "├─ " + str(item))
741
-
742
- def json(self, data: Any, indent: int = 4):
743
- """Print formatted JSON data."""
744
- formatted = json.dumps(data, indent=indent)
745
- self.print(formatted, highlight=True)
746
-
747
- def code_block(self, code: str, language: str = "python"):
748
- """Print code in a fancy box with syntax highlighting."""
749
- highlighted = self._highlight_code(code, language)
750
- lines = highlighted.split('\n')
751
-
752
- width = max(len(line) for line in lines)
753
- width = min(width, self._get_terminal_width() - 4) # Account for borders
754
-
755
- print('┌' + '─' * width + '┐')
756
-
757
- for line in lines:
758
- if len(line) > width:
759
- line = line[:width-3] + '...'
760
- else:
761
- line = line + ' ' * (width - len(line))
762
- print('│ ' + line + ' │')
763
-
764
- print('└' + '─' * width + '┘')
765
-
766
- def _clear_line(self):
767
- """Clear the current line."""
768
- print('\r' + ' ' * self._get_terminal_width(), end='\r', file=self.file, flush=True)
769
-
770
- if __name__ == "__main__":
771
- printer = LitPrinter()
772
-
773
- printer.banner("Welcome to the LitPrinter Demo! ")
774
-
775
- printer.status("Loading that heat... ")
776
- time.sleep(1)
777
- printer.status("Almost ready to drop... ")
778
- time.sleep(1)
779
- printer.status("")
780
-
781
- printer.success("Ayy, we made it! ")
782
- printer.error("Houston, we got a problem! ")
783
- printer.warning("Hold up, something sus... ")
784
- printer.info("Just so you know fam... ")
785
-
786
- headers = ["Name", "Vibe", "Energy"]
787
- rows = [
788
- ["Python", "Immaculate", "100%"],
789
- ["Java", "Decent", "75%"],
790
- ["C++", "Complex", "85%"]
791
- ]
792
- printer.table(headers, rows)
793
-
794
- data = {
795
- "squad": {
796
- "python": {"vibe": "lit", "power": "over 9000"},
797
- "javascript": {"vibe": "cool", "power": "8000"}
798
- },
799
- "config": {
800
- "mode": "beast",
801
- "activated": True
802
- }
803
- }
804
- printer.tree(data)
805
-
806
- printer.json(data)
807
-
808
- code = '''def print_drip():
809
- # This function brings the heat
810
- print("Straight bussin!")
811
- return True # No cap'''
812
- printer.code_block(code)
813
-
814
- printer.print("Basic text but make it fancy ")
815
- printer.print("Colors go hard", style=Colors.GREEN)
816
- printer.print("Bold & Blue = Different breed", style=Colors.BLUE + Colors.BOLD)
817
-
818
- markdown_text = """# Main Title (Straight Fire)
819
- ## Subtitle (Also Heat)
820
- - First thing's first
821
- - Second thing's second
822
- **Bold moves** and *smooth style*
823
- """
824
- printer.print(markdown_text, markdown=True)
825
-
826
- test_dict = {
827
- "name": "LitPrinter",
828
- "vibes": ["immaculate", "unmatched", "different"],
829
- "config": {"mode": "beast", "level": "over 9000"}
830
- }
831
- printer.print(test_dict)
1
+ """
2
+ Welcome to WebScout's Advanced Console Printer!
3
+
4
+ About This Printer:
5
+ - A powerful, feature-rich console output manager
6
+ - Created by Abhay Koul as part of the WebScout project
7
+ - Designed for beautiful, informative, and interactive terminal output
8
+
9
+ Core Features:
10
+ >>> printer.success("Operation completed successfully! ")
11
+ >>> printer.warning("Important notice! ")
12
+ >>> printer.info("Here's something interesting ")
13
+ >>> printer.error("Houston, we have a problem! ")
14
+
15
+ Special Features:
16
+ - Rich ANSI color support
17
+ - Data visualization tools
18
+ - Progress bars and spinners
19
+ - Code syntax highlighting
20
+ - Markdown rendering
21
+ - JSON pretty printing
22
+
23
+ For the best experience, use a terminal that supports UTF-8 and ANSI colors.
24
+
25
+ >>> # Basic Printing with Style
26
+ >>> printer.print("Hello World!", color="blue", bold=True)
27
+ >>> printer.print("Important Notice", bg_color="yellow", italic=True)
28
+
29
+ >>> # Layout and Formatting
30
+ >>> printer.print("Centered Text", center=True, width=50)
31
+ >>> printer.print("Indented Text", indent=4, prefix="→ ")
32
+
33
+ >>> # Borders and Decorations
34
+ >>> printer.print("Special Message", border=True, rounded_corners=True)
35
+ >>> printer.print("Alert", border=True, border_color="red", double_border=True)
36
+
37
+ >>> # Animations
38
+ >>> printer.print("Loading...", animate=True, animation_type="typing")
39
+ >>> printer.print("Processing", animate=True, animation_speed=0.1)
40
+
41
+ >>> # Data Visualization
42
+ >>> data = {"status": "active", "users": 100}
43
+ >>> printer.print(data, as_json=True) # Pretty JSON
44
+ >>> printer.print(data, as_tree=True) # Tree View
45
+
46
+ >>> # Code Highlighting
47
+ >>> code = '''def greet(): print("Hello!")'''
48
+ >>> printer.print(code, as_code=True, language="python")
49
+
50
+ >>> # Tables
51
+ >>> headers = ["Name", "Status"]
52
+ >>> rows = [["Server", "Online"]]
53
+ >>> printer.print([headers, rows], as_table=True)
54
+
55
+ >>> # Markdown Support
56
+ >>> printer.print("# Title\n- List item", markdown=True)
57
+ """
58
+
59
+ import sys
60
+ import os
61
+ import json
62
+ import time
63
+ import threading
64
+ import re
65
+ from typing import Any, Optional, TextIO, Union, Sequence, Dict, List
66
+ from datetime import datetime
67
+ import textwrap
68
+ from collections import defaultdict
69
+ import shutil
70
+ import inspect
71
+ from .colors import Colors
72
+
73
+ try:
74
+ from pygments import highlight
75
+ from pygments.lexers import get_lexer_by_name
76
+ from pygments.formatters import Terminal256Formatter
77
+ PYGMENTS_AVAILABLE = True
78
+ except ImportError:
79
+ PYGMENTS_AVAILABLE = False
80
+
81
+ # Enable UTF-8 output on Windows
82
+ if sys.platform == 'win32':
83
+ import ctypes
84
+ kernel32 = ctypes.windll.kernel32
85
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
86
+ sys.stdout.reconfigure(encoding='utf-8')
87
+
88
+ # Enable ANSI escape sequences for Windows
89
+ if os.name == 'nt':
90
+ import ctypes
91
+ kernel32 = ctypes.windll.kernel32
92
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
93
+
94
+ # Removed the duplicated Colors class to import from colors.py
95
+
96
+ class SyntaxTheme:
97
+ """Syntax highlighting theme."""
98
+ KEYWORD = Colors.MAGENTA + Colors.BOLD
99
+ STRING = Colors.GREEN
100
+ NUMBER = Colors.CYAN
101
+ COMMENT = Colors.BRIGHT_BLACK + Colors.ITALIC
102
+ FUNCTION = Colors.BRIGHT_BLUE
103
+ CLASS = Colors.BRIGHT_YELLOW + Colors.BOLD
104
+ OPERATOR = Colors.WHITE
105
+ BRACKET = Colors.WHITE
106
+ VARIABLE = Colors.BRIGHT_WHITE
107
+
108
+ class MarkdownTheme:
109
+ """Theme for markdown elements."""
110
+ H1 = Colors.BOLD + Colors.BLUE
111
+ H2 = Colors.BOLD + Colors.CYAN
112
+ H3 = Colors.BOLD + Colors.GREEN
113
+ BOLD = Colors.BOLD
114
+ ITALIC = Colors.ITALIC
115
+ CODE = Colors.YELLOW
116
+ LINK = Colors.BLUE + Colors.UNDERLINE
117
+ LIST_BULLET = Colors.CYAN + "•" + Colors.RESET
118
+ QUOTE = Colors.GRAY
119
+ STRIKE = Colors.STRIKE
120
+ TABLE = Colors.GREEN
121
+ TASK = Colors.YELLOW
122
+ DETAILS = Colors.MAGENTA
123
+
124
+ class ThemeStyles:
125
+ SUCCESS = f"{Colors.GREEN}{Colors.BOLD}"
126
+ ERROR = f"{Colors.RED}{Colors.BOLD}"
127
+ WARNING = f"{Colors.YELLOW}{Colors.BOLD}"
128
+ INFO = f"{Colors.BLUE}{Colors.BOLD}"
129
+ DEBUG = f"{Colors.MAGENTA}"
130
+ CODE = f"{Colors.CYAN}"
131
+
132
+ class HoverInfo:
133
+ """Hover information for different elements."""
134
+ BANNER = "A fancy banner to make your output pop!"
135
+ SUCCESS = "Something went right! "
136
+ ERROR = "Oops! Something went wrong "
137
+ WARNING = "Heads up! Something needs attention "
138
+ INFO = "Just some helpful info "
139
+ TABLE = "Data organized in rows and columns "
140
+ TREE = "Hierarchical data visualization "
141
+ JSON = "Pretty-printed JSON data "
142
+ CODE = "Syntax-highlighted code block "
143
+
144
+ class ProgressBar:
145
+ def __init__(self, total: int, width: int = 40, prefix: str = '', suffix: str = ''):
146
+ self.total = total
147
+ self.width = width
148
+ self.prefix = prefix
149
+ self.suffix = suffix
150
+ self.current = 0
151
+
152
+ def update(self, current: int):
153
+ self.current = current
154
+ filled = int(self.width * current / self.total)
155
+ bar = f"{Colors.GREEN}{'█' * filled}{Colors.RESET}{'░' * (self.width - filled)}"
156
+ percent = f"{Colors.CYAN}{int(100 * current / self.total)}%{Colors.RESET}"
157
+ print(f'\r{self.prefix} |{bar}| {percent} {self.suffix}', end='', flush=True)
158
+ if current >= self.total:
159
+ print()
160
+
161
+ class Spinner:
162
+ def __init__(self, message: str = ''):
163
+ self.message = message
164
+ self.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
165
+ self.running = False
166
+ self.thread = None
167
+
168
+ def spin(self):
169
+ while self.running:
170
+ for frame in self.frames:
171
+ if not self.running:
172
+ break
173
+ print(f'\r{Colors.CYAN}{frame}{Colors.RESET} {self.message}', end='', flush=True)
174
+ time.sleep(0.1)
175
+
176
+ def __enter__(self):
177
+ self.start()
178
+ return self
179
+
180
+ def __exit__(self, exc_type, exc_val, exc_tb):
181
+ self.stop()
182
+
183
+ def start(self):
184
+ self.running = True
185
+ self.thread = threading.Thread(target=self.spin)
186
+ self.thread.start()
187
+
188
+ def stop(self):
189
+ self.running = False
190
+ if self.thread:
191
+ self.thread.join()
192
+ print('\r' + ' ' * (len(self.message) + 2), end='', flush=True)
193
+ print('\r', end='')
194
+
195
+ class LitPrinter:
196
+ def __init__(self,
197
+ file: TextIO = sys.stdout,
198
+ theme: Optional[dict] = None,
199
+ indent_size: int = 4,
200
+ buffer_size: int = 1024,
201
+ syntax_theme: Optional[Dict[str, str]] = None,
202
+ markdown_theme: Optional[Dict[str, str]] = None):
203
+ self.file = file
204
+ self.indent_size = indent_size
205
+ self.buffer_size = buffer_size
206
+ self._terminal_width = shutil.get_terminal_size().columns
207
+ self._last_line = ""
208
+ self._spinner_active = False
209
+ self._progress_active = False
210
+
211
+ # Default theme
212
+ self.theme = theme or {
213
+ "str": Colors.WHITE,
214
+ "int": Colors.CYAN,
215
+ "float": Colors.CYAN,
216
+ "bool": Colors.YELLOW,
217
+ "list": Colors.MAGENTA,
218
+ "dict": Colors.BLUE,
219
+ "none": Colors.RED,
220
+ "timestamp": Colors.GREEN,
221
+ "key": Colors.YELLOW,
222
+ "bracket": Colors.BLUE,
223
+ "comma": Colors.WHITE,
224
+ "colon": Colors.WHITE,
225
+ "url": f"{Colors.BLUE}{Colors.UNDERLINE}",
226
+ "number": Colors.CYAN,
227
+ "special": Colors.MAGENTA + Colors.BOLD,
228
+ "error": Colors.RED + Colors.BOLD,
229
+ "warning": Colors.YELLOW + Colors.BOLD,
230
+ "success": Colors.GREEN + Colors.BOLD,
231
+ "info": Colors.BLUE + Colors.BOLD,
232
+ }
233
+
234
+ self.syntax_theme = syntax_theme or vars(SyntaxTheme)
235
+ self.markdown_theme = markdown_theme or vars(MarkdownTheme)
236
+
237
+ def _get_terminal_width(self) -> int:
238
+ """Get the terminal width or default to 80."""
239
+ try:
240
+ width = shutil.get_terminal_size().columns
241
+ return width if width > 0 else 80
242
+ except:
243
+ return 80
244
+
245
+ def _format_dict(self, d: dict, indent_level: int = 0) -> str:
246
+ """Format dictionary with proper indentation and colors."""
247
+ if not d:
248
+ return f"{self.theme['bracket']}{{}}{Colors.RESET}"
249
+
250
+ indent = " " * (self.indent_size * indent_level)
251
+ next_indent = " " * (self.indent_size * (indent_level + 1))
252
+
253
+ lines = [f"{self.theme['bracket']}{{{Colors.RESET}"]
254
+
255
+ for i, (key, value) in enumerate(d.items()):
256
+ # Format key with quotes if it's a string
257
+ if isinstance(key, str):
258
+ formatted_key = f"{self.theme['key']}'{key}'{Colors.RESET}"
259
+ else:
260
+ formatted_key = f"{self.theme['key']}{key}{Colors.RESET}"
261
+
262
+ # Format value based on type
263
+ if isinstance(value, dict):
264
+ formatted_value = self._format_dict(value, indent_level + 1)
265
+ elif isinstance(value, str):
266
+ # Special handling for URLs
267
+ if any(url_prefix in value.lower() for url_prefix in ['http://', 'https://', 'www.']):
268
+ formatted_value = f"{self.theme['url']}{value}{Colors.RESET}"
269
+ else:
270
+ # Word wrap long strings
271
+ if len(value) > 80:
272
+ wrapped = textwrap.fill(value, width=80, subsequent_indent=next_indent + " ")
273
+ formatted_value = f"{self.theme['str']}'{wrapped}'{Colors.RESET}"
274
+ else:
275
+ formatted_value = f"{self.theme['str']}'{value}'{Colors.RESET}"
276
+ elif isinstance(value, (int, float)):
277
+ formatted_value = f"{self.theme['number']}{value}{Colors.RESET}"
278
+ elif isinstance(value, bool):
279
+ formatted_value = f"{self.theme['bool']}{value}{Colors.RESET}"
280
+ elif value is None:
281
+ formatted_value = f"{self.theme['none']}None{Colors.RESET}"
282
+ else:
283
+ formatted_value = self._format_value(value)
284
+
285
+ # Add comma if not last item
286
+ comma = f"{self.theme['comma']},{Colors.RESET}" if i < len(d) - 1 else ""
287
+
288
+ lines.append(f"{next_indent}{formatted_key}{self.theme['colon']}: {Colors.RESET}{formatted_value}{comma}")
289
+
290
+ lines.append(f"{indent}{self.theme['bracket']}}}{Colors.RESET}")
291
+ return '\n'.join(lines)
292
+
293
+ def _format_sequence(self, seq: Sequence, indent_level: int = 0) -> str:
294
+ """Format sequences (lists, tuples, sets) with proper indentation."""
295
+ if not seq:
296
+ return f"{self.theme['bracket']}[]{Colors.RESET}"
297
+
298
+ indent = " " * (self.indent_size * indent_level)
299
+ next_indent = " " * (self.indent_size * (indent_level + 1))
300
+
301
+ lines = [f"{self.theme['bracket']}[{Colors.RESET}"]
302
+
303
+ for i, item in enumerate(seq):
304
+ formatted_item = self._format_value(item, indent_level + 1)
305
+ comma = f"{self.theme['comma']},{Colors.RESET}" if i < len(seq) - 1 else ""
306
+ lines.append(f"{next_indent}{formatted_item}{comma}")
307
+
308
+ lines.append(f"{indent}{self.theme['bracket']}]{Colors.RESET}")
309
+ return '\n'.join(lines)
310
+
311
+ def _format_value(self, value: Any, indent_level: int = 0) -> str:
312
+ """Enhanced format for any value with proper indentation and styling."""
313
+ if value is None:
314
+ return f"{self.theme['none']}None{Colors.RESET}"
315
+
316
+ if isinstance(value, dict):
317
+ return self._format_dict(value, indent_level)
318
+
319
+ if isinstance(value, (list, tuple, set)):
320
+ return self._format_sequence(value, indent_level)
321
+
322
+ if isinstance(value, str):
323
+ return str(value)
324
+
325
+ if isinstance(value, bool):
326
+ return f"{self.theme['bool']}{str(value)}{Colors.RESET}"
327
+
328
+ if isinstance(value, (int, float)):
329
+ return f"{self.theme['number']}{str(value)}{Colors.RESET}"
330
+
331
+ if hasattr(value, '__dict__'):
332
+ return self._format_dict(value.__dict__, indent_level)
333
+
334
+ return str(value)
335
+
336
+ def _highlight_code(self, code: str, language: str = "python") -> str:
337
+ """Print code with that extra drip """
338
+ if PYGMENTS_AVAILABLE:
339
+ try:
340
+ lexer = get_lexer_by_name(language)
341
+ formatter = Terminal256Formatter(style='monokai')
342
+ return highlight(code, lexer, formatter)
343
+ except:
344
+ pass
345
+
346
+ if language != 'python':
347
+ return code
348
+
349
+ lines = []
350
+ for line in code.split('\n'):
351
+ if '#' in line:
352
+ code_part, comment_part = line.split('#', 1)
353
+ line = code_part + f"{self.syntax_theme['COMMENT']}#{comment_part}{Colors.RESET}"
354
+
355
+ line = re.sub(r'(".*?"|\'.*?\')',
356
+ f"{self.syntax_theme['STRING']}\\1{Colors.RESET}", line)
357
+
358
+ line = re.sub(r'\b(\d+)\b',
359
+ f"{self.syntax_theme['NUMBER']}\\1{Colors.RESET}", line)
360
+
361
+ keywords = ['def', 'class', 'if', 'else', 'elif', 'for', 'while', 'try',
362
+ 'except', 'finally', 'with', 'as', 'import', 'from', 'return']
363
+
364
+ for keyword in keywords:
365
+ line = re.sub(f'\\b{keyword}\\b',
366
+ f"{self.syntax_theme['KEYWORD']}{keyword}{Colors.RESET}", line)
367
+
368
+ lines.append(line)
369
+
370
+ return '\n'.join(lines)
371
+
372
+ def _format_markdown_stream(self, text: str) -> str:
373
+ """Enhanced markdown formatting for streaming mode."""
374
+ # Headers with emoji flair
375
+ text = re.sub(r'^# (.+)$', f"{self.markdown_theme['H1']}🔥 \\1{Colors.RESET}", text, flags=re.M)
376
+ text = re.sub(r'^## (.+)$', f"{self.markdown_theme['H2']}✨ \\1{Colors.RESET}", text, flags=re.M)
377
+ text = re.sub(r'^### (.+)$', f"{self.markdown_theme['H3']}💫 \\1{Colors.RESET}", text, flags=re.M)
378
+
379
+ # Bold, italic, and combined with multiple styles
380
+ text = re.sub(r'\*\*\*(.+?)\*\*\*', f"{Colors.BOLD}{Colors.ITALIC}\\1{Colors.RESET}", text)
381
+ text = re.sub(r'\*\*(.+?)\*\*', f"{Colors.BOLD}\\1{Colors.RESET}", text)
382
+ text = re.sub(r'\*(.+?)\*', f"{Colors.ITALIC}\\1{Colors.RESET}", text)
383
+ text = re.sub(r'__(.+?)__', f"{Colors.BOLD}\\1{Colors.RESET}", text)
384
+ text = re.sub(r'_(.+?)_', f"{Colors.ITALIC}\\1{Colors.RESET}", text)
385
+
386
+ # Code blocks and inline code
387
+ text = re.sub(r'```(\w+)?\n(.*?)\n```', lambda m: self._highlight_code(m.group(2), m.group(1) or 'text'), text, flags=re.S)
388
+ text = re.sub(r'`(.+?)`', f"{Colors.CYAN}\\1{Colors.RESET}", text)
389
+
390
+ # Lists with proper indentation and bullets
391
+ lines = text.split('\n')
392
+ formatted_lines = []
393
+ for i, line in enumerate(lines):
394
+ # Match different bullet point styles
395
+ bullet_match = re.match(r'^(\s*)([-•*]|\d+\.) (.+)$', line)
396
+ if bullet_match:
397
+ indent, bullet, content = bullet_match.groups()
398
+ indent_level = len(indent) // 2
399
+
400
+ # Choose bullet style based on nesting level
401
+ if indent_level == 0:
402
+ bullet_style = "•"
403
+ elif indent_level == 1:
404
+ bullet_style = ""
405
+ else:
406
+ bullet_style = ""
407
+
408
+ # Format the line with proper indentation and bullet
409
+ formatted_line = f"{' ' * (indent_level * 2)}{Colors.CYAN}{bullet_style}{Colors.RESET} {content}"
410
+ formatted_lines.append(formatted_line)
411
+ else:
412
+ formatted_lines.append(line)
413
+ text = '\n'.join(formatted_lines)
414
+
415
+ # Links with underline
416
+ text = re.sub(r'\[(.+?)\]\((.+?)\)', f"{Colors.BLUE}{Colors.UNDERLINE}\\1{Colors.RESET}", text)
417
+
418
+ # Blockquotes with style
419
+ text = re.sub(r'^> (.+)$', f"{self.markdown_theme['QUOTE']}│ \\1{Colors.RESET}", text, flags=re.M)
420
+
421
+ # Strikethrough
422
+ text = re.sub(r'~~(.+?)~~', f"{Colors.STRIKE}\\1{Colors.RESET}", text)
423
+
424
+ # Task lists with fancy checkboxes
425
+ text = re.sub(r'- \[ \] (.+)$', f"{self.markdown_theme['TASK']}☐ \\1{Colors.RESET}", text, flags=re.M)
426
+ text = re.sub(r'- \[x\] (.+)$', f"{self.markdown_theme['TASK']} \\1{Colors.RESET}", text, flags=re.M)
427
+
428
+ # Tables with borders
429
+ table_pattern = r'\|(.+?)\|[\r\n]+\|[-:| ]+\|[\r\n]+((?:\|.+?\|[\r\n]+)+)'
430
+ text = re.sub(table_pattern, self._format_table_markdown, text, flags=re.M)
431
+
432
+ return text
433
+
434
+ def _format_table_markdown(self, match) -> str:
435
+ """Format markdown tables with style."""
436
+ header = [cell.strip() for cell in match.group(1).split('|') if cell.strip()]
437
+ rows = []
438
+ for row in match.group(2).strip().split('\n'):
439
+ cells = [cell.strip() for cell in row.split('|')[1:-1]]
440
+ if cells:
441
+ rows.append(cells)
442
+
443
+ # Get column widths
444
+ widths = [max(len(str(row[i])) for row in [header] + rows) for i in range(len(header))]
445
+
446
+ # Build table
447
+ result = []
448
+ # Header
449
+ result.append('┌' + '┬'.join('─' * (w + 2) for w in widths) + '┐')
450
+ result.append('│ ' + ' │ '.join(f"{h:<{w}}" for h, w in zip(header, widths)) + ' │')
451
+ result.append('├' + '┼'.join('─' * (w + 2) for w in widths) + '┤')
452
+ # Rows
453
+ for row in rows:
454
+ result.append('│ ' + ' │ '.join(f"{str(c):<{w}}" for c, w in zip(row, widths)) + ' │')
455
+ result.append('└' + '┴'.join('─' * (w + 2) for w in widths) + '┘')
456
+
457
+ return '\n'.join(result)
458
+
459
+ def print(self, *args,
460
+ # Builtin print compatibility
461
+ sep: str = " ",
462
+ end: str = "\n",
463
+ file: Optional[TextIO] = None,
464
+ flush: bool = True,
465
+
466
+ # Styling options
467
+ style: Optional[str] = None,
468
+ color: Optional[str] = None,
469
+ bg_color: Optional[str] = None,
470
+ bold: bool = False,
471
+ italic: bool = False,
472
+ underline: bool = False,
473
+ blink: bool = False,
474
+ strike: bool = False,
475
+ dim: bool = False,
476
+ reverse: bool = False,
477
+
478
+ # Layout options
479
+ markdown: Optional[bool] = None,
480
+ highlight: bool = False,
481
+ center: bool = False,
482
+ indent: int = 0,
483
+ prefix: Optional[str] = None,
484
+ suffix: Optional[str] = None,
485
+ width: Optional[int] = None,
486
+ padding: int = 0,
487
+ margin: int = 0,
488
+ align: str = "left",
489
+
490
+ # Border options
491
+ border: bool = False,
492
+ border_style: Optional[str] = None,
493
+ border_char: str = "─",
494
+ border_color: Optional[str] = None,
495
+ rounded_corners: bool = False,
496
+ double_border: bool = False,
497
+
498
+ # Animation options
499
+ animate: bool = False,
500
+ animation_speed: float = 0.05,
501
+ animation_type: str = "typing",
502
+
503
+ # Special features
504
+ as_table: bool = False,
505
+ as_tree: bool = False,
506
+ as_json: bool = False,
507
+ as_code: bool = False,
508
+ language: str = "python",
509
+
510
+ # Advanced features
511
+ raw: bool = False) -> None:
512
+ """
513
+ Enhanced print with all builtin features plus rich formatting.
514
+
515
+ Supports all builtin print parameters plus rich formatting features.
516
+ Automatically detects and formats markdown content unless explicitly disabled.
517
+ """
518
+ # Handle raw output mode
519
+ if raw:
520
+ print(*args, sep=sep, end=end, file=file or self.file, flush=flush)
521
+ return
522
+
523
+ # Join args with separator
524
+ output = sep.join(str(arg) for arg in args)
525
+
526
+ # Auto-detect markdown if not explicitly set
527
+ if markdown is None:
528
+ markdown = any(marker in output for marker in [
529
+ '#', '*', '_', '`', '>', '-', '•', '|',
530
+ # '✨', '🔥', '💫', '☐', '☑',
531
+ 'http://', 'https://',
532
+ '```', '~~~',
533
+ '<details>', '<summary>',
534
+ ])
535
+
536
+ # Apply markdown formatting if enabled
537
+ if markdown:
538
+ output = self._format_markdown_stream(output)
539
+
540
+ # Build style string
541
+ style_str = style or ""
542
+ if color:
543
+ style_str += getattr(Colors, color.upper(), "")
544
+ if bg_color:
545
+ style_str += getattr(Colors, f"BG_{bg_color.upper()}", "")
546
+ if bold:
547
+ style_str += Colors.BOLD
548
+ if italic:
549
+ style_str += Colors.ITALIC
550
+ if underline:
551
+ style_str += Colors.UNDERLINE
552
+ if blink:
553
+ style_str += Colors.BLINK
554
+ if strike:
555
+ style_str += Colors.STRIKE
556
+ if dim:
557
+ style_str += Colors.DIM
558
+ if reverse:
559
+ style_str += Colors.REVERSE
560
+
561
+ # Apply style if any
562
+ if style_str:
563
+ output = f"{style_str}{output}{Colors.RESET}"
564
+
565
+ # Handle special formatting
566
+ if as_json:
567
+ output = self._format_json(output)
568
+ elif as_code:
569
+ output = self._highlight_code(output, language)
570
+ elif as_table:
571
+ if isinstance(output, (list, tuple)):
572
+ self.table(*output)
573
+ return
574
+ elif as_tree:
575
+ if isinstance(output, (dict, list)):
576
+ self.tree(output)
577
+ return
578
+
579
+ # Apply layout options
580
+ if center:
581
+ term_width = self._get_terminal_width()
582
+ output = output.center(term_width)
583
+ if indent > 0:
584
+ output = " " * (indent * 4) + output
585
+ if prefix:
586
+ output = prefix + output
587
+ if suffix:
588
+ output = output + suffix
589
+ if width:
590
+ output = textwrap.fill(output, width=width)
591
+
592
+ # Add borders
593
+ if border:
594
+ width = max(len(line) for line in output.split('\n'))
595
+ border_top = '┌' + border_char * width + '┐'
596
+ border_bottom = '└' + border_char * width + '┘'
597
+ output = f"{border_top}\n{output}\n{border_bottom}"
598
+
599
+ # Handle animation
600
+ if animate:
601
+ for char in output:
602
+ print(char, end="", flush=True)
603
+ time.sleep(animation_speed)
604
+ print(end=end, flush=flush)
605
+ return
606
+
607
+ # Final output
608
+ print(output, end=end, file=file or self.file, flush=flush)
609
+
610
+ def status(self, text: str, style: Optional[str] = None):
611
+ """Print a status message that can be overwritten."""
612
+ style = style or self.theme['info']
613
+ self._clear_line()
614
+ self._last_line = f"{style}{text}{Colors.RESET}"
615
+ print(self._last_line, end='\r', file=self.file, flush=True)
616
+
617
+ def banner(self, text: str, style: Optional[str] = None):
618
+ """Print a fancy banner with hover info."""
619
+ print(f"\033]1337;Custom=id=banner:{HoverInfo.BANNER}\a", end='')
620
+ style = style or self.theme['special']
621
+ width = self._get_terminal_width() - 4
622
+
623
+ lines = textwrap.wrap(text, width=width, break_long_words=False)
624
+
625
+ print('╔' + '═' * width + '╗')
626
+ for line in lines:
627
+ padding = width - len(line)
628
+ print('║ ' + line + ' ' * padding + ' ║')
629
+ print('╚' + '═' * width + '╝')
630
+ print("\033]1337;Custom=id=banner:end\a", end='')
631
+
632
+ def success(self, text: str):
633
+ """Print a success message with hover info."""
634
+ print(f"\033]1337;Custom=id=success:{HoverInfo.SUCCESS}\a", end='')
635
+ self.print(f" {text}", style=self.theme['success'])
636
+ print("\033]1337;Custom=id=success:end\a", end='')
637
+
638
+ def error(self, text: str):
639
+ """Print an error message with hover info."""
640
+ print(f"\033]1337;Custom=id=error:{HoverInfo.ERROR}\a", end='')
641
+ self.print(f"✗ {text}", style=self.theme['error'])
642
+ print("\033]1337;Custom=id=error:end\a", end='')
643
+
644
+ def warning(self, text: str):
645
+ """Print a warning message with hover info."""
646
+ print(f"\033]1337;Custom=id=warning:{HoverInfo.WARNING}\a", end='')
647
+ self.print(f"⚠ {text}", style=self.theme['warning'])
648
+ print("\033]1337;Custom=id=warning:end\a", end='')
649
+
650
+ def info(self, text: str):
651
+ """Print an info message with hover info."""
652
+ print(f"\033]1337;Custom=id=info:{HoverInfo.INFO}\a", end='')
653
+ self.print(f"ℹ {text}", style=self.theme['info'])
654
+ print("\033]1337;Custom=id=info:end\a", end='')
655
+
656
+ def table(self, headers: List[str], rows: List[List[Any]], style: Optional[str] = None):
657
+ """Print a formatted table."""
658
+ style = style or self.theme['special']
659
+
660
+ col_widths = [len(h) for h in headers]
661
+ for row in rows:
662
+ for i, cell in enumerate(row):
663
+ col_widths[i] = max(col_widths[i], len(str(cell)))
664
+
665
+ header_line = '| ' + ' | '.join(f"{h:<{w}}" for h, w in zip(headers, col_widths)) + ' |'
666
+ print(header_line)
667
+
668
+ separator = '+' + '+'.join('-' * (w + 2) for w in col_widths) + '+'
669
+ print(separator)
670
+
671
+ for row in rows:
672
+ row_line = '| ' + ' | '.join(f"{str(cell):<{w}}" for cell, w in zip(row, col_widths)) + ' |'
673
+ print(row_line)
674
+
675
+ def tree(self, data: Union[Dict, List], indent: int = 0):
676
+ """Print a tree structure of nested data."""
677
+ if isinstance(data, dict):
678
+ for key, value in data.items():
679
+ self.print(" " * indent + "├─ " + str(key) + ":", style=self.theme['key'])
680
+ if isinstance(value, (dict, list)):
681
+ self.tree(value, indent + 1)
682
+ else:
683
+ self.print(" " * (indent + 1) + "└─ " + str(value))
684
+ elif isinstance(data, list):
685
+ for item in data:
686
+ if isinstance(item, (dict, list)):
687
+ self.tree(item, indent + 1)
688
+ else:
689
+ self.print(" " * indent + "├─ " + str(item))
690
+
691
+ def json(self, data: Any, indent: int = 4):
692
+ """Print formatted JSON data."""
693
+ formatted = json.dumps(data, indent=indent)
694
+ self.print(formatted, highlight=True)
695
+
696
+ def code_block(self, code: str, language: str = "python"):
697
+ """Print code in a fancy box with syntax highlighting."""
698
+ highlighted = self._highlight_code(code, language)
699
+ lines = highlighted.split('\n')
700
+
701
+ width = max(len(line) for line in lines)
702
+ width = min(width, self._get_terminal_width() - 4) # Account for borders
703
+
704
+ print('┌' + '' * width + '┐')
705
+
706
+ for line in lines:
707
+ if len(line) > width:
708
+ line = line[:width-3] + '...'
709
+ else:
710
+ line = line + ' ' * (width - len(line))
711
+ print('│ ' + line + ' │')
712
+
713
+ print('└' + '─' * width + '┘')
714
+
715
+ def _clear_line(self):
716
+ """Clear the current line."""
717
+ print('\r' + ' ' * self._get_terminal_width(), end='\r', file=self.file, flush=True)
718
+
719
+