Newer
Older
navi-1 / clients / terminal / tui / renderers / planning.py
"""Renderers for planning events (planning_status, plan_ready)."""

from __future__ import annotations

from rich.box import ROUNDED
from rich.console import RenderableType
from rich.markdown import Markdown
from rich.panel import Panel
from rich.text import Text

from clients.terminal.tui.themes import get_active_theme

from .base import ContentRenderer
from .markdown_content import ThemedMarkdownRenderable, _theme_aware_code_theme


class PlanningStatusRenderer(ContentRenderer):
    """Render a planning-phase progress line.

    Non-subagent planning is a transient indicator that rolls through phases
    (Analysis → Execution plan → Plan review) in one line; subagent planning is
    shown dimmed so it does not bleed into the parent turn's indicator.
    """

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

    def render(self, msg: dict) -> RenderableType:
        theme = get_active_theme()
        label = msg.get("label", "") or "planning…"
        is_subagent = bool(msg.get("is_subagent", False))
        prefix = "(subagent) " if is_subagent else ""
        color = theme.text_dim if is_subagent else theme.info
        return Text(f"{prefix}⚙ Planning · {label}", style=color.hex)


class PlanReadyRenderer(ContentRenderer):
    """Render the finalized plan as a card.

    The plan text is markdown (## Plan, **Task:**, **Steps:** …) and is rendered
    with the themed Markdown renderer inside a panel. Subagent plans use a dim
    border so they read as nested context, not the main turn's plan.
    """

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

    def render(self, msg: dict) -> RenderableType:
        theme = get_active_theme()
        plan = msg.get("plan", "") or ""
        is_subagent = bool(msg.get("is_subagent", False))
        code_theme = _theme_aware_code_theme(theme.name)
        body = ThemedMarkdownRenderable(
            Markdown(plan, code_theme=code_theme, inline_code_theme=code_theme),
            theme.name,
        )
        title = "subagent plan" if is_subagent else "Plan"
        border = theme.text_dim if is_subagent else theme.accent
        return Panel(
            body,
            title=title,
            title_align="left",
            border_style=border.hex,
            box=ROUNDED,
        )