mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-05 20:40:09 -08:00
163 lines
4.1 KiB
Python
163 lines
4.1 KiB
Python
"""
|
|
ANSI utilities for FZF preview scripts.
|
|
|
|
Lightweight stdlib-only utilities to replace Rich dependency in preview scripts.
|
|
Provides RGB color formatting, table rendering, and markdown stripping.
|
|
"""
|
|
|
|
import re
|
|
import shutil
|
|
import textwrap
|
|
|
|
|
|
def rgb_color(r: int, g: int, b: int, text: str, bold: bool = False) -> str:
|
|
"""
|
|
Format text with RGB color using ANSI escape codes.
|
|
|
|
Args:
|
|
r: Red component (0-255)
|
|
g: Green component (0-255)
|
|
b: Blue component (0-255)
|
|
text: Text to colorize
|
|
bold: Whether to make text bold
|
|
|
|
Returns:
|
|
ANSI-escaped colored text
|
|
"""
|
|
color_code = f"\x1b[38;2;{r};{g};{b}m"
|
|
bold_code = "\x1b[1m" if bold else ""
|
|
reset = "\x1b[0m"
|
|
return f"{color_code}{bold_code}{text}{reset}"
|
|
|
|
|
|
def parse_color(color_csv: str) -> tuple[int, int, int]:
|
|
"""
|
|
Parse RGB color from comma-separated string.
|
|
|
|
Args:
|
|
color_csv: Color as 'R,G,B' string
|
|
|
|
Returns:
|
|
Tuple of (r, g, b) integers
|
|
"""
|
|
parts = color_csv.split(",")
|
|
return int(parts[0]), int(parts[1]), int(parts[2])
|
|
|
|
|
|
def print_rule(sep_color: str) -> None:
|
|
"""
|
|
Print a horizontal rule line.
|
|
|
|
Args:
|
|
sep_color: Color as 'R,G,B' string
|
|
"""
|
|
width = shutil.get_terminal_size((80, 24)).columns
|
|
r, g, b = parse_color(sep_color)
|
|
print(rgb_color(r, g, b, "─" * width))
|
|
|
|
|
|
def print_table_row(
|
|
key: str, value: str, header_color: str, key_width: int, value_width: int
|
|
) -> None:
|
|
"""
|
|
Print a two-column table row with left-aligned key and right-aligned value.
|
|
|
|
Args:
|
|
key: Left column text (header/key)
|
|
value: Right column text (value)
|
|
header_color: Color for key as 'R,G,B' string
|
|
key_width: Width for key column
|
|
value_width: Width for value column
|
|
"""
|
|
r, g, b = parse_color(header_color)
|
|
key_styled = rgb_color(r, g, b, key, bold=True)
|
|
|
|
# Get actual terminal width
|
|
term_width = shutil.get_terminal_size((80, 24)).columns
|
|
|
|
# Calculate actual value width based on terminal and key
|
|
actual_value_width = max(20, term_width - len(key) - 2)
|
|
|
|
# Wrap value if it's too long
|
|
value_lines = textwrap.wrap(str(value), width=actual_value_width) if value else [""]
|
|
|
|
if not value_lines:
|
|
value_lines = [""]
|
|
|
|
# Print first line with properly aligned value
|
|
first_line = value_lines[0]
|
|
# Use manual spacing to right-align
|
|
spacing = term_width - len(key) - len(first_line) - 2
|
|
if spacing > 0:
|
|
print(f"{key_styled} {' ' * spacing}{first_line}")
|
|
else:
|
|
print(f"{key_styled} {first_line}")
|
|
|
|
# Print remaining wrapped lines (left-aligned, indented)
|
|
for line in value_lines[1:]:
|
|
print(f"{' ' * (len(key) + 2)}{line}")
|
|
|
|
|
|
def strip_markdown(text: str) -> str:
|
|
"""
|
|
Strip markdown formatting from text.
|
|
|
|
Removes:
|
|
- Headers (# ## ###)
|
|
- Bold (**text** or __text__)
|
|
- Italic (*text* or _text_)
|
|
- Links ([text](url))
|
|
- Code blocks (```code```)
|
|
- Inline code (`code`)
|
|
|
|
Args:
|
|
text: Markdown-formatted text
|
|
|
|
Returns:
|
|
Plain text with markdown removed
|
|
"""
|
|
if not text:
|
|
return ""
|
|
|
|
# Remove code blocks first
|
|
text = re.sub(r"```[\s\S]*?```", "", text)
|
|
|
|
# Remove inline code
|
|
text = re.sub(r"`([^`]+)`", r"\1", text)
|
|
|
|
# Remove headers
|
|
text = re.sub(r"^#{1,6}\s+", "", text, flags=re.MULTILINE)
|
|
|
|
# Remove bold (** or __)
|
|
text = re.sub(r"\*\*(.+?)\*\*", r"\1", text)
|
|
text = re.sub(r"__(.+?)__", r"\1", text)
|
|
|
|
# Remove italic (* or _)
|
|
text = re.sub(r"\*(.+?)\*", r"\1", text)
|
|
text = re.sub(r"_(.+?)_", r"\1", text)
|
|
|
|
# Remove links, keep text
|
|
text = re.sub(r"\[(.+?)\]\(.+?\)", r"\1", text)
|
|
|
|
# Remove images
|
|
text = re.sub(r"!\[.*?\]\(.+?\)", "", text)
|
|
|
|
return text.strip()
|
|
|
|
|
|
def wrap_text(text: str, width: int | None = None) -> str:
|
|
"""
|
|
Wrap text to terminal width.
|
|
|
|
Args:
|
|
text: Text to wrap
|
|
width: Width to wrap to (defaults to terminal width)
|
|
|
|
Returns:
|
|
Wrapped text
|
|
"""
|
|
if width is None:
|
|
width = shutil.get_terminal_size((80, 24)).columns
|
|
|
|
return textwrap.fill(text, width=width)
|