Newer
Older
navi-1 / clients / terminal / tui / renderers / markdown_content.py
"""Renderer for markdown content."""

from __future__ import annotations

from rich.console import Console, RenderableType, ConsoleOptions, RenderResult
from rich.markdown import Markdown
from rich.segment import Segment
from rich.style import Style
from rich.theme import Theme as RichTheme

from clients.terminal.tui.themes import get_active_theme

from .base import ContentRenderer


def _theme_aware_code_theme(theme_name: str) -> str:
    """Pick a Pygments code theme that matches the Navi theme brightness."""
    return "dracula" if theme_name == "gnexus-dark" else "github-light"


class ThemedMarkdownRenderable:
    """Wrap Markdown and render it with Navi theme colors applied."""

    def __init__(self, markdown: Markdown, theme_name: str) -> None:
        self._markdown = markdown
        self._theme_name = theme_name

    def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
        theme = get_active_theme()
        themed_console = Console(
            width=console.width,
            color_system=console.color_system,
            theme=RichTheme(theme.rich_theme_styles()),
            force_terminal=True,
        )
        segments = list(themed_console.render(self._markdown))
        link_color = Style.parse(theme.link.hex).color
        for segment in segments:
            style = segment.style
            if style and style.link and link_color is not None:
                segment = Segment(
                    segment.text,
                    Style(color=link_color, underline=True, link=style.link),
                )
            yield segment


class MarkdownRenderer(ContentRenderer):
    """Render markdown text with syntax highlighting."""

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

    def render(self, msg: dict) -> RenderableType:
        theme = get_active_theme()
        text = msg.get("content", "")
        code_theme = _theme_aware_code_theme(theme.name)
        md = Markdown(
            text,
            code_theme=code_theme,
            inline_code_theme=code_theme,
        )
        return ThemedMarkdownRenderable(md, theme.name)