Files
spec-kit/src/apm_cli/utils/console.py
2025-09-16 13:56:00 +02:00

159 lines
4.5 KiB
Python

"""Console utility functions for formatting and output."""
import click
import sys
from typing import Optional, Any
# Rich library imports with fallbacks
try:
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich import print as rich_print
RICH_AVAILABLE = True
except ImportError:
RICH_AVAILABLE = False
Console = Any
Panel = Any
Table = Any
rich_print = None
# Colorama imports for fallback
try:
from colorama import Fore, Style, init
init(autoreset=True)
COLORAMA_AVAILABLE = True
except ImportError:
COLORAMA_AVAILABLE = False
Fore = None
Style = None
# Status symbols for consistent iconography
STATUS_SYMBOLS = {
'success': '',
'sparkles': '',
'running': '🚀',
'gear': '⚙️',
'info': '💡',
'warning': '⚠️',
'error': '',
'check': '',
'list': '📋',
'preview': '👀',
'robot': '🤖',
'metrics': '📊'
}
def _get_console() -> Optional[Any]:
"""Get Rich console instance if available."""
if RICH_AVAILABLE:
try:
return Console()
except Exception:
pass
return None
def _rich_echo(message: str, color: str = "white", style: str = None, bold: bool = False, symbol: str = None):
"""Echo message with Rich formatting or colorama fallback."""
# Handle backward compatibility - if style is provided, use it as color
if style is not None:
color = style
if symbol and symbol in STATUS_SYMBOLS:
symbol_char = STATUS_SYMBOLS[symbol]
message = f"{symbol_char} {message}"
console = _get_console()
if console:
try:
style_str = color
if bold:
style_str = f"bold {color}"
console.print(message, style=style_str)
return
except Exception:
pass
# Colorama fallback
if COLORAMA_AVAILABLE and Fore:
color_map = {
'red': Fore.RED,
'green': Fore.GREEN,
'yellow': Fore.YELLOW,
'blue': Fore.BLUE,
'cyan': Fore.CYAN,
'white': Fore.WHITE,
'magenta': Fore.MAGENTA,
'muted': Fore.WHITE, # Add muted mapping
'info': Fore.BLUE
}
color_code = color_map.get(color, Fore.WHITE)
style_code = Style.BRIGHT if bold else ""
click.echo(f"{color_code}{style_code}{message}{Style.RESET_ALL}")
else:
click.echo(message)
def _rich_success(message: str, symbol: str = None):
"""Display success message with green color and bold styling."""
_rich_echo(message, color="green", symbol=symbol, bold=True)
def _rich_error(message: str, symbol: str = None):
"""Display error message with red color."""
_rich_echo(message, color="red", symbol=symbol)
def _rich_warning(message: str, symbol: str = None):
"""Display warning message with yellow color."""
_rich_echo(message, color="yellow", symbol=symbol)
def _rich_info(message: str, symbol: str = None):
"""Display info message with blue color."""
_rich_echo(message, color="blue", symbol=symbol)
def _rich_panel(content: str, title: str = None, style: str = "cyan"):
"""Display content in a Rich panel with fallback."""
console = _get_console()
if console and Panel:
try:
panel = Panel(content, title=title, border_style=style)
console.print(panel)
return
except Exception:
pass
# Fallback to simple text display
if title:
click.echo(f"\n--- {title} ---")
click.echo(content)
if title:
click.echo("-" * (len(title) + 8))
def _create_files_table(files_data: list, title: str = "Files") -> Optional[Any]:
"""Create a Rich table for file display."""
if not RICH_AVAILABLE or not Table:
return None
try:
table = Table(title=f"📋 {title}", show_header=True, header_style="bold cyan")
table.add_column("File", style="bold white")
table.add_column("Description", style="white")
for file_info in files_data:
if isinstance(file_info, dict):
table.add_row(file_info.get('name', ''), file_info.get('description', ''))
elif isinstance(file_info, (list, tuple)) and len(file_info) >= 2:
table.add_row(str(file_info[0]), str(file_info[1]))
else:
table.add_row(str(file_info), "")
return table
except Exception:
return None