mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-19 02:36:41 +00:00
198 lines
5.8 KiB
Python
198 lines
5.8 KiB
Python
|
|
"""
|
||
|
|
Windows-compatible symbol fallbacks for SuperClaude UI
|
||
|
|
Handles Unicode encoding issues on Windows terminals
|
||
|
|
"""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
import platform
|
||
|
|
|
||
|
|
|
||
|
|
def can_display_unicode() -> bool:
|
||
|
|
"""
|
||
|
|
Detect if terminal can display Unicode symbols safely
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
True if Unicode is safe to use, False if fallbacks needed
|
||
|
|
"""
|
||
|
|
# Check if we're on Windows with problematic encoding
|
||
|
|
if platform.system() == "Windows":
|
||
|
|
# Check console encoding
|
||
|
|
try:
|
||
|
|
# Test if we can encode common Unicode symbols
|
||
|
|
test_symbols = "✓✗█░⠋═"
|
||
|
|
test_symbols.encode(sys.stdout.encoding or 'cp1252')
|
||
|
|
return True
|
||
|
|
except (UnicodeEncodeError, LookupError):
|
||
|
|
return False
|
||
|
|
|
||
|
|
# Check if stdout encoding supports Unicode
|
||
|
|
encoding = getattr(sys.stdout, 'encoding', None)
|
||
|
|
if encoding and encoding.lower() in ['utf-8', 'utf8']:
|
||
|
|
return True
|
||
|
|
|
||
|
|
# Conservative fallback for unknown systems
|
||
|
|
return False
|
||
|
|
|
||
|
|
|
||
|
|
class Symbols:
|
||
|
|
"""Cross-platform symbol definitions with Windows fallbacks"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.unicode_safe = can_display_unicode()
|
||
|
|
|
||
|
|
@property
|
||
|
|
def checkmark(self) -> str:
|
||
|
|
"""Success checkmark symbol"""
|
||
|
|
return "✓" if self.unicode_safe else "+"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def crossmark(self) -> str:
|
||
|
|
"""Error/failure cross symbol"""
|
||
|
|
return "✗" if self.unicode_safe else "x"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def block_filled(self) -> str:
|
||
|
|
"""Filled block for progress bars"""
|
||
|
|
return "█" if self.unicode_safe else "#"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def block_empty(self) -> str:
|
||
|
|
"""Empty block for progress bars"""
|
||
|
|
return "░" if self.unicode_safe else "-"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def double_line(self) -> str:
|
||
|
|
"""Double line separator"""
|
||
|
|
return "═" if self.unicode_safe else "="
|
||
|
|
|
||
|
|
@property
|
||
|
|
def spinner_chars(self) -> str:
|
||
|
|
"""Spinner animation characters"""
|
||
|
|
if self.unicode_safe:
|
||
|
|
return "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
||
|
|
else:
|
||
|
|
return "|/-\\|/-\\"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def box_top_left(self) -> str:
|
||
|
|
"""Box drawing: top-left corner"""
|
||
|
|
return "╔" if self.unicode_safe else "+"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def box_top_right(self) -> str:
|
||
|
|
"""Box drawing: top-right corner"""
|
||
|
|
return "╗" if self.unicode_safe else "+"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def box_bottom_left(self) -> str:
|
||
|
|
"""Box drawing: bottom-left corner"""
|
||
|
|
return "╚" if self.unicode_safe else "+"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def box_bottom_right(self) -> str:
|
||
|
|
"""Box drawing: bottom-right corner"""
|
||
|
|
return "╝" if self.unicode_safe else "+"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def box_horizontal(self) -> str:
|
||
|
|
"""Box drawing: horizontal line"""
|
||
|
|
return "═" if self.unicode_safe else "="
|
||
|
|
|
||
|
|
@property
|
||
|
|
def box_vertical(self) -> str:
|
||
|
|
"""Box drawing: vertical line"""
|
||
|
|
return "║" if self.unicode_safe else "|"
|
||
|
|
|
||
|
|
def make_separator(self, length: int) -> str:
|
||
|
|
"""Create a separator line of specified length"""
|
||
|
|
return self.double_line * length
|
||
|
|
|
||
|
|
def make_box_line(self, length: int) -> str:
|
||
|
|
"""Create a box horizontal line of specified length"""
|
||
|
|
return self.box_horizontal * length
|
||
|
|
|
||
|
|
|
||
|
|
# Global instance for easy import
|
||
|
|
symbols = Symbols()
|
||
|
|
|
||
|
|
|
||
|
|
def safe_print(*args, **kwargs):
|
||
|
|
"""
|
||
|
|
Print function that handles Unicode encoding errors gracefully
|
||
|
|
|
||
|
|
Falls back to ASCII-safe representation if Unicode fails
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
print(*args, **kwargs)
|
||
|
|
except UnicodeEncodeError:
|
||
|
|
# Convert arguments to safe strings
|
||
|
|
safe_args = []
|
||
|
|
for arg in args:
|
||
|
|
if isinstance(arg, str):
|
||
|
|
# Replace problematic Unicode characters
|
||
|
|
safe_arg = (arg
|
||
|
|
.replace("✓", "+")
|
||
|
|
.replace("✗", "x")
|
||
|
|
.replace("█", "#")
|
||
|
|
.replace("░", "-")
|
||
|
|
.replace("═", "=")
|
||
|
|
.replace("⠋", "|")
|
||
|
|
.replace("⠙", "/")
|
||
|
|
.replace("⠹", "-")
|
||
|
|
.replace("⠸", "\\")
|
||
|
|
.replace("⠼", "|")
|
||
|
|
.replace("⠴", "/")
|
||
|
|
.replace("⠦", "-")
|
||
|
|
.replace("⠧", "\\")
|
||
|
|
.replace("⠇", "|")
|
||
|
|
.replace("⠏", "/")
|
||
|
|
.replace("╔", "+")
|
||
|
|
.replace("╗", "+")
|
||
|
|
.replace("╚", "+")
|
||
|
|
.replace("╝", "+")
|
||
|
|
.replace("║", "|")
|
||
|
|
)
|
||
|
|
safe_args.append(safe_arg)
|
||
|
|
else:
|
||
|
|
safe_args.append(str(arg))
|
||
|
|
|
||
|
|
# Try printing with safe arguments
|
||
|
|
try:
|
||
|
|
print(*safe_args, **kwargs)
|
||
|
|
except UnicodeEncodeError:
|
||
|
|
# Last resort: encode to ASCII with replacement
|
||
|
|
final_args = []
|
||
|
|
for arg in safe_args:
|
||
|
|
if isinstance(arg, str):
|
||
|
|
final_args.append(arg.encode('ascii', 'replace').decode('ascii'))
|
||
|
|
else:
|
||
|
|
final_args.append(str(arg))
|
||
|
|
print(*final_args, **kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
def format_with_symbols(text: str) -> str:
|
||
|
|
"""
|
||
|
|
Replace Unicode symbols in text with Windows-compatible alternatives
|
||
|
|
"""
|
||
|
|
if symbols.unicode_safe:
|
||
|
|
return text
|
||
|
|
|
||
|
|
# Replace symbols with safe alternatives
|
||
|
|
replacements = {
|
||
|
|
"✓": symbols.checkmark,
|
||
|
|
"✗": symbols.crossmark,
|
||
|
|
"█": symbols.block_filled,
|
||
|
|
"░": symbols.block_empty,
|
||
|
|
"═": symbols.double_line,
|
||
|
|
"╔": symbols.box_top_left,
|
||
|
|
"╗": symbols.box_top_right,
|
||
|
|
"╚": symbols.box_bottom_left,
|
||
|
|
"╝": symbols.box_bottom_right,
|
||
|
|
"║": symbols.box_vertical,
|
||
|
|
}
|
||
|
|
|
||
|
|
for unicode_char, safe_char in replacements.items():
|
||
|
|
text = text.replace(unicode_char, safe_char)
|
||
|
|
|
||
|
|
return text
|