Newer
Older
navi-1 / clients / terminal / tui / renderers / tool.py
"""Renderers for tool_started and tool_call events."""

from __future__ import annotations

import json

from rich.console import RenderableType
from rich.json import JSON as RichJSON
from rich.box import ROUNDED
from rich.padding import Padding
from rich.panel import Panel
from rich.text import Text

from clients.terminal.tui.themes import get_active_theme

from .base import ContentRenderer


class ToolStartedRenderer(ContentRenderer):
    """Render a tool call start marker.

    Sub-agent tool calls (``is_subagent=True``) are indented to read as nested
    inside the parent spawn_agent card.
    """

    def accepts(self, msg: dict) -> bool:
        return msg.get("type") == "tool_started"

    def render(self, msg: dict) -> RenderableType:
        theme = get_active_theme()
        tool = msg.get("tool", "?")
        args = msg.get("args") or {}
        title = f"→ {tool}"
        if args:
            try:
                body = RichJSON(json.dumps(args))
            except Exception:
                body = Text(str(args), style=theme.text_dim.hex)
        else:
            body = Text("")
        panel = Panel(
            body,
            title=title,
            title_align="left",
            border_style=theme.tool_border.hex,
            box=ROUNDED,
        )
        if bool(msg.get("is_subagent", False)):
            return Padding(panel, (0, 0, 0, 2))
        return panel


class ToolResultRenderer(ContentRenderer):
    """Render a tool call result.

    Sub-agent tool results (``is_subagent=True``) are indented to read as nested
    inside the parent spawn_agent card.
    """

    def accepts(self, msg: dict) -> bool:
        return msg.get("type") == "tool_call"

    def render(self, msg: dict) -> RenderableType:
        theme = get_active_theme()
        tool = msg.get("tool", "?")
        success = msg.get("success", True)
        result = msg.get("result")
        color = theme.tool_success if success else theme.tool_error
        title = f"← {tool} {'✓' if success else '✗'}"
        body = Text(str(result) if result is not None else "", style=theme.text_dim.hex)
        panel = Panel(
            body,
            title=title,
            title_align="left",
            border_style=color.hex,
            box=ROUNDED,
        )
        if bool(msg.get("is_subagent", False)):
            return Padding(panel, (0, 0, 0, 2))
        return panel