"""Modal screens for the LLM Router TUI. This module is allowed to import ``textual`` — it is part of the TUI and is never imported by the service dispatch path. """ from __future__ import annotations import json from typing import Any from textual.app import ComposeResult from textual.containers import Vertical, VerticalScroll from textual.screen import ModalScreen from textual.widgets import Button, Static class DecisionDetailScreen(ModalScreen[None]): """Popup showing the full route_decisions row for one decision. Press ``enter``, ``escape`` or ``q`` to close. """ CSS = """ DecisionDetailScreen { align: center middle; } #detail-container { width: 80; height: 36; border: thick $background 80%; padding: 1 2; background: $surface; } #detail-title { text-style: bold; color: $accent; height: auto; margin: 0 0 1 0; } #detail-content { height: 1fr; width: 1fr; } #detail-close { width: 100%; margin: 1 0 0 0; } """ def __init__(self, decision: dict[str, Any]) -> None: self.decision = decision super().__init__() def compose(self) -> ComposeResult: with Vertical(id="detail-container"): yield Static("Decision details", id="detail-title") with VerticalScroll(id="detail-content"): yield Static(self._render_text()) yield Button("Close (enter/esc/q)", id="detail-close") def _render_text(self) -> str: return json.dumps(self.decision, indent=2, default=str) def on_key(self, event) -> None: if event.key in ("escape", "q", "enter"): self.dismiss(None) def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "detail-close": self.dismiss(None)