"""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[bool | str]):
"""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")