feat(tui): add syntax highlighting for tool renderers (#195)

Add Pygments-based syntax highlighting with native hacker theme:
- Python renderer: Python code highlighting
- Browser renderer: JavaScript code highlighting
- Terminal renderer: Bash command highlighting
- File edit renderer: Auto-detect language from file extension, diff-style display
This commit is contained in:
Ahmed Allam
2025-12-14 04:39:28 +04:00
committed by GitHub
parent 5e3d14a1eb
commit a075ea1a0a
4 changed files with 194 additions and 16 deletions
@@ -1,16 +1,53 @@
from functools import cache
from typing import Any, ClassVar
from pygments.lexers import PythonLexer
from pygments.styles import get_style_by_name
from textual.widgets import Static
from .base_renderer import BaseToolRenderer
from .registry import register_tool_renderer
@cache
def _get_style_colors() -> dict[Any, str]:
style = get_style_by_name("native")
return {token: f"#{style_def['color']}" for token, style_def in style if style_def["color"]}
@register_tool_renderer
class PythonRenderer(BaseToolRenderer):
tool_name: ClassVar[str] = "python_action"
css_classes: ClassVar[list[str]] = ["tool-call", "python-tool"]
@classmethod
def _get_token_color(cls, token_type: Any) -> str | None:
colors = _get_style_colors()
while token_type:
if token_type in colors:
return colors[token_type]
token_type = token_type.parent
return None
@classmethod
def _highlight_python(cls, code: str) -> str:
lexer = PythonLexer()
result_parts: list[str] = []
for token_type, token_value in lexer.get_tokens(code):
if not token_value:
continue
escaped_value = cls.escape_markup(token_value)
color = cls._get_token_color(token_type)
if color:
result_parts.append(f"[{color}]{escaped_value}[/]")
else:
result_parts.append(escaped_value)
return "".join(result_parts)
@classmethod
def render(cls, tool_data: dict[str, Any]) -> Static:
args = tool_data.get("args", {})
@@ -21,8 +58,9 @@ class PythonRenderer(BaseToolRenderer):
header = "</> [bold #3b82f6]Python[/]"
if code and action in ["new_session", "execute"]:
code_display = code[:600] + "..." if len(code) > 600 else code
content_text = f"{header}\n [italic white]{cls.escape_markup(code_display)}[/]"
code_display = code[:2000] + "..." if len(code) > 2000 else code
highlighted_code = cls._highlight_python(code_display)
content_text = f"{header}\n{highlighted_code}"
elif action == "close":
content_text = f"{header}\n [dim]Closing session...[/]"
elif action == "list_sessions":