Newer
Older
navi-1 / clients / terminal / tui / screens / permission_dialog.py
"""Modal permission dialog for destructive tool operations."""

from __future__ import annotations

from textual.app import ComposeResult
from textual.containers import Grid, Horizontal
from textual.screen import ModalScreen
from textual.widgets import Button, Static


class PermissionDialogScreen(ModalScreen[str | None]):
    """Ask user whether to allow a potentially destructive tool call.

    Returns:
        "allow_once" | "allow_always" | "deny_once" | "deny_always" | None (dismissed)
    """

    DEFAULT_CSS = """
    PermissionDialogScreen { align: center middle; }
    PermissionDialogScreen > Grid {
        grid-size: 1;
        grid-gutter: 1 2;
        padding: 1 2;
        border: thick $tui-error;
        background: $tui-surface;
        width: 60;
        height: auto;
    }
    PermissionDialogScreen > Grid > Static {
        width: 100%;
        color: $tui-text;
    }
    PermissionDialogScreen .tool-name {
        color: $tui-error;
        text-style: bold;
    }
    PermissionDialogScreen .details {
        color: $tui-text-dim;
    }
    PermissionDialogScreen .buttons { height: auto; }
    PermissionDialogScreen Button {
        margin: 0 1;
    }
    """

    def __init__(
        self,
        tool: str,
        action: str,
        target: str,
        details: str,
    ) -> None:
        super().__init__()
        self._tool = tool
        self._action = action
        self._target = target
        self._details = details

    def compose(self) -> ComposeResult:
        with Grid():
            yield Static("Permission required", classes="tool-name")
            yield Static(f"Tool: {self._tool}", classes="details")
            if self._action:
                yield Static(f"Action: {self._action}", classes="details")
            if self._target:
                yield Static(f"Target: {self._target}", classes="details")
            if self._details:
                yield Static(self._details, classes="details")
            with Horizontal(classes="buttons"):
                yield Button("Allow once", id="allow_once", variant="primary")
                yield Button("Always allow", id="allow_always", variant="primary")
                yield Button("Deny", id="deny_once", variant="error")
                yield Button("Always deny", id="deny_always", variant="error")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        self.dismiss(event.button.id)

    def on_key(self, event) -> None:
        if event.key == "escape":
            self.dismiss("deny_once")