diff --git a/crawl4ai/async_database.py b/crawl4ai/async_database.py index 870350e9..a41ca97f 100644 --- a/crawl4ai/async_database.py +++ b/crawl4ai/async_database.py @@ -171,7 +171,10 @@ class AsyncDatabaseManager: f"Code context:\n{error_context['code_context']}" ) self.logger.error( - message=create_box_message(error_message, type="error"), + message="{error}", + tag="ERROR", + params={"error": str(error_message)}, + boxes=["error"], ) raise @@ -189,7 +192,10 @@ class AsyncDatabaseManager: f"Code context:\n{error_context['code_context']}" ) self.logger.error( - message=create_box_message(error_message, type="error"), + message="{error}", + tag="ERROR", + params={"error": str(error_message)}, + boxes=["error"], ) raise finally: diff --git a/crawl4ai/async_logger.py b/crawl4ai/async_logger.py index 273ef53b..7866e36f 100644 --- a/crawl4ai/async_logger.py +++ b/crawl4ai/async_logger.py @@ -1,9 +1,11 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import Optional, Dict, Any -from colorama import Fore, Style, init +from typing import Optional, Dict, Any, List import os from datetime import datetime +from rich.console import Console +from rich.text import Text +from .utils import create_box_message class LogLevel(Enum): @@ -13,6 +15,8 @@ class LogLevel(Enum): WARNING = 4 ERROR = 5 + def __str__(self): + return self.name.lower() class AsyncLoggerBase(ABC): @@ -64,11 +68,11 @@ class AsyncLogger(AsyncLoggerBase): } DEFAULT_COLORS = { - LogLevel.DEBUG: Fore.LIGHTBLACK_EX, - LogLevel.INFO: Fore.CYAN, - LogLevel.SUCCESS: Fore.GREEN, - LogLevel.WARNING: Fore.YELLOW, - LogLevel.ERROR: Fore.RED, + LogLevel.DEBUG: "lightblack", + LogLevel.INFO: "cyan", + LogLevel.SUCCESS: "green", + LogLevel.WARNING: "yellow", + LogLevel.ERROR: "red", } def __init__( @@ -91,13 +95,13 @@ class AsyncLogger(AsyncLoggerBase): colors: Custom colors for different log levels verbose: Whether to output to console """ - init() # Initialize colorama self.log_file = log_file self.log_level = log_level self.tag_width = tag_width self.icons = icons or self.DEFAULT_ICONS self.colors = colors or self.DEFAULT_COLORS self.verbose = verbose + self.console = Console() # Create log file directory if needed if log_file: @@ -114,16 +118,11 @@ class AsyncLogger(AsyncLoggerBase): def _write_to_file(self, message: str): """Write a message to the log file if configured.""" if self.log_file: + text = Text.from_markup(message) + plain_text = text.plain timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] with open(self.log_file, "a", encoding="utf-8") as f: - # Strip ANSI color codes for file output - clean_message = message.replace(Fore.RESET, "").replace( - Style.RESET_ALL, "" - ) - for color in vars(Fore).values(): - if isinstance(color, str): - clean_message = clean_message.replace(color, "") - f.write(f"[{timestamp}] {clean_message}\n") + f.write(f"[{timestamp}] {plain_text}\n") def _log( self, @@ -132,6 +131,7 @@ class AsyncLogger(AsyncLoggerBase): tag: str, params: Optional[Dict[str, Any]] = None, colors: Optional[Dict[str, str]] = None, + boxes: Optional[List[str]] = None, base_color: Optional[str] = None, **kwargs, ): @@ -144,55 +144,41 @@ class AsyncLogger(AsyncLoggerBase): tag: Tag for the message params: Parameters to format into the message colors: Color overrides for specific parameters + boxes: Box overrides for specific parameters base_color: Base color for the entire message """ if level.value < self.log_level.value: return - # Format the message with parameters if provided + # avoid conflict with rich formatting + parsed_message = message.replace("[", "[[").replace("]", "]]") + raw_message = message.format(**params) if params else message if params: - try: - # First format the message with raw parameters - formatted_message = message.format(**params) - - # Then apply colors if specified - color_map = { - "green": Fore.GREEN, - "red": Fore.RED, - "yellow": Fore.YELLOW, - "blue": Fore.BLUE, - "cyan": Fore.CYAN, - "magenta": Fore.MAGENTA, - "white": Fore.WHITE, - "black": Fore.BLACK, - "reset": Style.RESET_ALL, - } - if colors: - for key, color in colors.items(): - # Find the formatted value in the message and wrap it with color - if color in color_map: - color = color_map[color] - if key in params: - value_str = str(params[key]) - formatted_message = formatted_message.replace( - value_str, f"{color}{value_str}{Style.RESET_ALL}" - ) - - except KeyError as e: - formatted_message = ( - f"LOGGING ERROR: Missing parameter {e} in message template" - ) - level = LogLevel.ERROR + formatted_message = parsed_message.format(**params) + for key, value in params.items(): + # value_str may discard `[` and `]`, so we need to replace it. + value_str = str(value).replace("[", "[[").replace("]", "]]") + # check is need apply color + if colors and key in colors: + color_str = f"[{colors[key]}]{value_str}[/{colors[key]}]" + formatted_message = formatted_message.replace(value_str, color_str) + value_str = color_str + + # check is need apply box + if boxes and key in boxes: + formatted_message = formatted_message.replace(value_str, + create_box_message(value_str, type=str(level))) + else: - formatted_message = message + formatted_message = parsed_message # Construct the full log line color = base_color or self.colors[level] - log_line = f"{color}{self._format_tag(tag)} {self._get_icon(tag)} {formatted_message}{Style.RESET_ALL}" + log_line = f"[{color}]{self._format_tag(tag)} {self._get_icon(tag)} {formatted_message} [/{color}]" # Output to console if verbose if self.verbose or kwargs.get("force_verbose", False): - print(log_line) + self.console.print(log_line) # Write to file if configured self._write_to_file(log_line) @@ -246,8 +232,8 @@ class AsyncLogger(AsyncLoggerBase): "timing": timing, }, colors={ - "status": Fore.GREEN if success else Fore.RED, - "timing": Fore.YELLOW, + "status": "green" if success else "red", + "timing": "yellow", }, ) @@ -268,6 +254,7 @@ class AsyncLogger(AsyncLoggerBase): message="{url:.{url_length}}... | Error: {error}", tag=tag, params={"url": url, "url_length": url_length, "error": error}, + boxes=["error"], ) class AsyncFileLogger(AsyncLoggerBase): diff --git a/crawl4ai/async_webcrawler.py b/crawl4ai/async_webcrawler.py index 16bd5f57..963c2d05 100644 --- a/crawl4ai/async_webcrawler.py +++ b/crawl4ai/async_webcrawler.py @@ -2,7 +2,6 @@ from .__version__ import __version__ as crawl4ai_version import os import sys import time -from colorama import Fore from pathlib import Path from typing import Optional, List import json @@ -382,8 +381,8 @@ class AsyncWebCrawler: "timing": f"{time.perf_counter() - start_time:.2f}s", }, colors={ - "status": Fore.GREEN if crawl_result.success else Fore.RED, - "timing": Fore.YELLOW, + "status": "green" if crawl_result.success else "red", + "timing": "yellow", }, ) @@ -402,7 +401,7 @@ class AsyncWebCrawler: "status": True, "timing": f"{time.perf_counter() - start_time:.2f}s", }, - colors={"status": Fore.GREEN, "timing": Fore.YELLOW}, + colors={"status": "green", "timing": "yellow"}, ) cached_result.success = bool(html) @@ -422,7 +421,7 @@ class AsyncWebCrawler: self.logger.error_status( url=url, - error=create_box_message(error_message, type="error"), + error=error_message, tag="ERROR", ) diff --git a/crawl4ai/browser_profiler.py b/crawl4ai/browser_profiler.py index 2291faa2..f8b9e2b0 100644 --- a/crawl4ai/browser_profiler.py +++ b/crawl4ai/browser_profiler.py @@ -15,8 +15,8 @@ import shutil import json import subprocess import time -from typing import List, Dict, Optional, Any, Tuple -from colorama import Fore, Style, init +from typing import List, Dict, Optional, Any +from rich.console import Console from .async_configs import BrowserConfig from .browser_manager import ManagedBrowser @@ -45,8 +45,8 @@ class BrowserProfiler: logger (AsyncLoggerBase, optional): Logger for outputting messages. If None, a default AsyncLogger will be created. """ - # Initialize colorama for colorful terminal output - init() + # Initialize rich console for colorful input prompts + self.console = Console() # Create a logger if not provided if logger is None: @@ -127,18 +127,18 @@ class BrowserProfiler: profile_path = os.path.join(self.profiles_dir, profile_name) os.makedirs(profile_path, exist_ok=True) - # Print instructions for the user with colorama formatting - border = f"{Fore.CYAN}{'='*80}{Style.RESET_ALL}" - self.logger.info(f"\n{border}", tag="PROFILE") - self.logger.info(f"Creating browser profile: {Fore.GREEN}{profile_name}{Style.RESET_ALL}", tag="PROFILE") - self.logger.info(f"Profile directory: {Fore.YELLOW}{profile_path}{Style.RESET_ALL}", tag="PROFILE") + # Print instructions for the user with rich formatting + border = "{'='*80}" + self.logger.info("{border}", tag="PROFILE", params={"border": f"\n{border}"}, colors={"border": "cyan"}) + self.logger.info("Creating browser profile: {profile_name}", tag="PROFILE", params={"profile_name": profile_name}, colors={"profile_name": "green"}) + self.logger.info("Profile directory: {profile_path}", tag="PROFILE", params={"profile_path": profile_path}, colors={"profile_path": "yellow"}) self.logger.info("\nInstructions:", tag="PROFILE") self.logger.info("1. A browser window will open for you to set up your profile.", tag="PROFILE") - self.logger.info(f"2. {Fore.CYAN}Log in to websites{Style.RESET_ALL}, configure settings, etc. as needed.", tag="PROFILE") - self.logger.info(f"3. When you're done, {Fore.YELLOW}press 'q' in this terminal{Style.RESET_ALL} to close the browser.", tag="PROFILE") + self.logger.info("{segment}, configure settings, etc. as needed.", tag="PROFILE", params={"segment": "2. Log in to websites"}, colors={"segment": "cyan"}) + self.logger.info("3. When you're done, {segment} to close the browser.", tag="PROFILE", params={"segment": "press 'q' in this terminal"}, colors={"segment": "yellow"}) self.logger.info("4. The profile will be saved and ready to use with Crawl4AI.", tag="PROFILE") - self.logger.info(f"{border}\n", tag="PROFILE") + self.logger.info("{border}", tag="PROFILE", params={"border": f"{border}\n"}, colors={"border": "cyan"}) # Create managed browser instance managed_browser = ManagedBrowser( @@ -181,7 +181,7 @@ class BrowserProfiler: import select # First output the prompt - self.logger.info(f"{Fore.CYAN}Press '{Fore.WHITE}q{Fore.CYAN}' when you've finished using the browser...{Style.RESET_ALL}", tag="PROFILE") + self.logger.info("Press 'q' when you've finished using the browser...", tag="PROFILE") # Save original terminal settings fd = sys.stdin.fileno() @@ -197,7 +197,7 @@ class BrowserProfiler: if readable: key = sys.stdin.read(1) if key.lower() == 'q': - self.logger.info(f"{Fore.GREEN}Closing browser and saving profile...{Style.RESET_ALL}", tag="PROFILE") + self.logger.info("Closing browser and saving profile...", tag="PROFILE", base_color="green") user_done_event.set() return @@ -223,7 +223,7 @@ class BrowserProfiler: self.logger.error("Failed to start browser process.", tag="PROFILE") return None - self.logger.info(f"Browser launched. {Fore.CYAN}Waiting for you to finish...{Style.RESET_ALL}", tag="PROFILE") + self.logger.info(f"Browser launched. Waiting for you to finish...", tag="PROFILE") # Start listening for keyboard input listener_task = asyncio.create_task(listen_for_quit_command()) @@ -245,10 +245,10 @@ class BrowserProfiler: self.logger.info("Terminating browser process...", tag="PROFILE") await managed_browser.cleanup() - self.logger.success(f"Browser closed. Profile saved at: {Fore.GREEN}{profile_path}{Style.RESET_ALL}", tag="PROFILE") + self.logger.success(f"Browser closed. Profile saved at: {profile_path}", tag="PROFILE") except Exception as e: - self.logger.error(f"Error creating profile: {str(e)}", tag="PROFILE") + self.logger.error(f"Error creating profile: {e!s}", tag="PROFILE") await managed_browser.cleanup() return None finally: @@ -440,25 +440,27 @@ class BrowserProfiler: ``` """ while True: - self.logger.info(f"\n{Fore.CYAN}Profile Management Options:{Style.RESET_ALL}", tag="MENU") - self.logger.info(f"1. {Fore.GREEN}Create a new profile{Style.RESET_ALL}", tag="MENU") - self.logger.info(f"2. {Fore.YELLOW}List available profiles{Style.RESET_ALL}", tag="MENU") - self.logger.info(f"3. {Fore.RED}Delete a profile{Style.RESET_ALL}", tag="MENU") + self.logger.info(f"\nProfile Management Options:", tag="MENU") + self.logger.info(f"1. Create a new profile", tag="MENU", base_color="green") + self.logger.info(f"2. List available profiles", tag="MENU", base_color="yellow") + self.logger.info(f"3. Delete a profile", tag="MENU", base_color="red") # Only show crawl option if callback provided if crawl_callback: - self.logger.info(f"4. {Fore.CYAN}Use a profile to crawl a website{Style.RESET_ALL}", tag="MENU") - self.logger.info(f"5. {Fore.MAGENTA}Exit{Style.RESET_ALL}", tag="MENU") + self.logger.info(f"4. Use a profile to crawl a website", tag="MENU", base_color="cyan") + self.logger.info(f"5. Exit", tag="MENU", base_color="magenta") exit_option = "5" else: - self.logger.info(f"4. {Fore.MAGENTA}Exit{Style.RESET_ALL}", tag="MENU") + self.logger.info(f"4. Exit", tag="MENU", base_color="magenta") exit_option = "4" - choice = input(f"\n{Fore.CYAN}Enter your choice (1-{exit_option}): {Style.RESET_ALL}") + self.logger.print(f"\n[cyan]Enter your choice (1-{exit_option}): [/cyan]", end="") + choice = input() if choice == "1": # Create new profile - name = input(f"{Fore.GREEN}Enter a name for the new profile (or press Enter for auto-generated name): {Style.RESET_ALL}") + self.console.print("[green]Enter a name for the new profile (or press Enter for auto-generated name): [/green]", end="") + name = input() await self.create_profile(name or None) elif choice == "2": @@ -472,8 +474,8 @@ class BrowserProfiler: # Print profile information with colorama formatting self.logger.info("\nAvailable profiles:", tag="PROFILES") for i, profile in enumerate(profiles): - self.logger.info(f"[{i+1}] {Fore.CYAN}{profile['name']}{Style.RESET_ALL}", tag="PROFILES") - self.logger.info(f" Path: {Fore.YELLOW}{profile['path']}{Style.RESET_ALL}", tag="PROFILES") + self.logger.info(f"[{i+1}] {profile['name']}", tag="PROFILES") + self.logger.info(f" Path: {profile['path']}", tag="PROFILES", base_color="yellow") self.logger.info(f" Created: {profile['created'].strftime('%Y-%m-%d %H:%M:%S')}", tag="PROFILES") self.logger.info(f" Browser type: {profile['type']}", tag="PROFILES") self.logger.info("", tag="PROFILES") # Empty line for spacing @@ -486,12 +488,13 @@ class BrowserProfiler: continue # Display numbered list - self.logger.info(f"\n{Fore.YELLOW}Available profiles:{Style.RESET_ALL}", tag="PROFILES") + self.logger.info(f"\nAvailable profiles:", tag="PROFILES", base_color="yellow") for i, profile in enumerate(profiles): self.logger.info(f"[{i+1}] {profile['name']}", tag="PROFILES") # Get profile to delete - profile_idx = input(f"{Fore.RED}Enter the number of the profile to delete (or 'c' to cancel): {Style.RESET_ALL}") + self.console.print("[red]Enter the number of the profile to delete (or 'c' to cancel): [/red]", end="") + profile_idx = input() if profile_idx.lower() == 'c': continue @@ -499,17 +502,18 @@ class BrowserProfiler: idx = int(profile_idx) - 1 if 0 <= idx < len(profiles): profile_name = profiles[idx]["name"] - self.logger.info(f"Deleting profile: {Fore.YELLOW}{profile_name}{Style.RESET_ALL}", tag="PROFILES") + self.logger.info(f"Deleting profile: [yellow]{profile_name}[/yellow]", tag="PROFILES") # Confirm deletion - confirm = input(f"{Fore.RED}Are you sure you want to delete this profile? (y/n): {Style.RESET_ALL}") + self.console.print("[red]Are you sure you want to delete this profile? (y/n): [/red]", end="") + confirm = input() if confirm.lower() == 'y': success = self.delete_profile(profiles[idx]["path"]) if success: - self.logger.success(f"Profile {Fore.GREEN}{profile_name}{Style.RESET_ALL} deleted successfully", tag="PROFILES") + self.logger.success(f"Profile {profile_name} deleted successfully", tag="PROFILES") else: - self.logger.error(f"Failed to delete profile {Fore.RED}{profile_name}{Style.RESET_ALL}", tag="PROFILES") + self.logger.error(f"Failed to delete profile {profile_name}", tag="PROFILES") else: self.logger.error("Invalid profile number", tag="PROFILES") except ValueError: @@ -523,12 +527,13 @@ class BrowserProfiler: continue # Display numbered list - self.logger.info(f"\n{Fore.YELLOW}Available profiles:{Style.RESET_ALL}", tag="PROFILES") + self.logger.info(f"\nAvailable profiles:", tag="PROFILES", base_color="yellow") for i, profile in enumerate(profiles): self.logger.info(f"[{i+1}] {profile['name']}", tag="PROFILES") # Get profile to use - profile_idx = input(f"{Fore.CYAN}Enter the number of the profile to use (or 'c' to cancel): {Style.RESET_ALL}") + self.console.print("[cyan]Enter the number of the profile to use (or 'c' to cancel): [/cyan]", end="") + profile_idx = input() if profile_idx.lower() == 'c': continue @@ -536,7 +541,8 @@ class BrowserProfiler: idx = int(profile_idx) - 1 if 0 <= idx < len(profiles): profile_path = profiles[idx]["path"] - url = input(f"{Fore.CYAN}Enter the URL to crawl: {Style.RESET_ALL}") + self.console.print("[cyan]Enter the URL to crawl: [/cyan]", end="") + url = input() if url: # Call the provided crawl callback await crawl_callback(profile_path, url) @@ -600,10 +606,10 @@ class BrowserProfiler: border = f"{Fore.CYAN}{'='*80}{Style.RESET_ALL}" self.logger.info(f"\n{border}", tag="CDP") self.logger.info(f"Launching standalone browser with CDP debugging", tag="CDP") - self.logger.info(f"Browser type: {Fore.GREEN}{browser_type}{Style.RESET_ALL}", tag="CDP") - self.logger.info(f"Profile path: {Fore.YELLOW}{profile_path}{Style.RESET_ALL}", tag="CDP") - self.logger.info(f"Debugging port: {Fore.CYAN}{debugging_port}{Style.RESET_ALL}", tag="CDP") - self.logger.info(f"Headless mode: {Fore.CYAN}{headless}{Style.RESET_ALL}", tag="CDP") + self.logger.info("Browser type: {browser_type}", tag="CDP", params={"browser_type": browser_type}, colors={"browser_type": "cyan"}) + self.logger.info("Profile path: {profile_path}", tag="CDP", params={"profile_path": profile_path}, colors={"profile_path": "yellow"}) + self.logger.info(f"Debugging port: {debugging_port}", tag="CDP") + self.logger.info(f"Headless mode: {headless}", tag="CDP") # Create managed browser instance managed_browser = ManagedBrowser( @@ -646,7 +652,7 @@ class BrowserProfiler: import select # First output the prompt - self.logger.info(f"{Fore.CYAN}Press '{Fore.WHITE}q{Fore.CYAN}' to stop the browser and exit...{Style.RESET_ALL}", tag="CDP") + self.logger.info("Press 'q' to stop the browser and exit...", tag="CDP") # Save original terminal settings fd = sys.stdin.fileno() @@ -662,7 +668,7 @@ class BrowserProfiler: if readable: key = sys.stdin.read(1) if key.lower() == 'q': - self.logger.info(f"{Fore.GREEN}Closing browser...{Style.RESET_ALL}", tag="CDP") + self.logger.info("Closing browser...", tag="CDP") user_done_event.set() return @@ -722,14 +728,14 @@ class BrowserProfiler: cdp_url, config_json = await get_cdp_json(debugging_port) if cdp_url: - self.logger.success(f"CDP URL: {Fore.GREEN}{cdp_url}{Style.RESET_ALL}", tag="CDP") + self.logger.success(f"CDP URL: {cdp_url}", tag="CDP") if config_json: # Display relevant CDP information - self.logger.info(f"Browser: {Fore.CYAN}{config_json.get('Browser', 'Unknown')}{Style.RESET_ALL}", tag="CDP") - self.logger.info(f"Protocol Version: {config_json.get('Protocol-Version', 'Unknown')}", tag="CDP") + self.logger.info(f"Browser: {config_json.get('Browser', 'Unknown')}", tag="CDP", colors={"Browser": "cyan"}) + self.logger.info(f"Protocol Version: {config_json.get('Protocol-Version', 'Unknown')}", tag="CDP", colors={"Protocol-Version": "cyan"}) if 'webSocketDebuggerUrl' in config_json: - self.logger.info(f"WebSocket URL: {Fore.GREEN}{config_json['webSocketDebuggerUrl']}{Style.RESET_ALL}", tag="CDP") + self.logger.info("WebSocket URL: {webSocketDebuggerUrl}", tag="CDP", params={"webSocketDebuggerUrl": config_json['webSocketDebuggerUrl']}, colors={"webSocketDebuggerUrl": "green"}) else: self.logger.warning("Could not retrieve CDP configuration JSON", tag="CDP") else: @@ -757,7 +763,7 @@ class BrowserProfiler: self.logger.info("Terminating browser process...", tag="CDP") await managed_browser.cleanup() - self.logger.success(f"Browser closed.", tag="CDP") + self.logger.success("Browser closed.", tag="CDP") except Exception as e: self.logger.error(f"Error launching standalone browser: {str(e)}", tag="CDP") diff --git a/crawl4ai/content_filter_strategy.py b/crawl4ai/content_filter_strategy.py index 8d7a51b4..35c6ce8c 100644 --- a/crawl4ai/content_filter_strategy.py +++ b/crawl4ai/content_filter_strategy.py @@ -28,7 +28,8 @@ import hashlib from pathlib import Path from concurrent.futures import ThreadPoolExecutor from .async_logger import AsyncLogger, LogLevel -from colorama import Fore, Style +from rich.console import Console +from rich.text import Text class RelevantContentFilter(ABC): @@ -846,8 +847,7 @@ class LLMContentFilter(RelevantContentFilter): }, colors={ **AsyncLogger.DEFAULT_COLORS, - LogLevel.INFO: Fore.MAGENTA - + Style.DIM, # Dimmed purple for LLM ops + LogLevel.INFO: "dim magenta" # Dimmed purple for LLM ops }, ) else: @@ -892,7 +892,7 @@ class LLMContentFilter(RelevantContentFilter): "Starting LLM markdown content filtering process", tag="LLM", params={"provider": self.llm_config.provider}, - colors={"provider": Fore.CYAN}, + colors={"provider": "cyan"}, ) # Cache handling @@ -929,7 +929,7 @@ class LLMContentFilter(RelevantContentFilter): "LLM markdown: Split content into {chunk_count} chunks", tag="CHUNK", params={"chunk_count": len(html_chunks)}, - colors={"chunk_count": Fore.YELLOW}, + colors={"chunk_count": "yellow"}, ) start_time = time.time() @@ -1038,7 +1038,7 @@ class LLMContentFilter(RelevantContentFilter): "LLM markdown: Completed processing in {time:.2f}s", tag="LLM", params={"time": end_time - start_time}, - colors={"time": Fore.YELLOW}, + colors={"time": "yellow"}, ) result = ordered_results if ordered_results else [] diff --git a/crawl4ai/utils.py b/crawl4ai/utils.py index 02d105a9..7b3057b3 100644 --- a/crawl4ai/utils.py +++ b/crawl4ai/utils.py @@ -20,7 +20,6 @@ from urllib.parse import urljoin import requests from requests.exceptions import InvalidSchema import xxhash -from colorama import Fore, Style, init import textwrap import cProfile import pstats @@ -441,14 +440,13 @@ def create_box_message( str: A formatted string containing the styled message box. """ - init() - # Define border and text colors for different types styles = { - "warning": (Fore.YELLOW, Fore.LIGHTYELLOW_EX, "⚠"), - "info": (Fore.BLUE, Fore.LIGHTBLUE_EX, "ℹ"), - "success": (Fore.GREEN, Fore.LIGHTGREEN_EX, "✓"), - "error": (Fore.RED, Fore.LIGHTRED_EX, "×"), + "warning": ("yellow", "bright_yellow", "⚠"), + "info": ("blue", "bright_blue", "ℹ"), + "debug": ("lightblack", "bright_black", "⋯"), + "success": ("green", "bright_green", "✓"), + "error": ("red", "bright_red", "×"), } border_color, text_color, prefix = styles.get(type.lower(), styles["info"]) @@ -480,12 +478,12 @@ def create_box_message( # Create the box with colored borders and lighter text horizontal_line = h_line * (width - 1) box = [ - f"{border_color}{tl}{horizontal_line}{tr}", + f"[{border_color}]{tl}{horizontal_line}{tr}[/{border_color}]", *[ - f"{border_color}{v_line}{text_color} {line:<{width-2}}{border_color}{v_line}" + f"[{border_color}]{v_line}[{text_color}] {line:<{width-2}}[/{text_color}][{border_color}]{v_line}[/{border_color}]" for line in formatted_lines ], - f"{border_color}{bl}{horizontal_line}{br}{Style.RESET_ALL}", + f"[{border_color}]{bl}{horizontal_line}{br}[/{border_color}]", ] result = "\n".join(box) @@ -2774,4 +2772,3 @@ def preprocess_html_for_schema(html_content, text_threshold=100, attr_value_thre # Fallback for parsing errors return html_content[:max_size] if len(html_content) > max_size else html_content -