Python · 2883 bytes Raw Blame History
1 """Confirmation modal for destructive tool operations."""
2
3 from rich.text import Text
4 from textual.app import ComposeResult
5 from textual.binding import Binding
6 from textual.containers import Horizontal, Vertical
7 from textual.screen import ModalScreen
8 from textual.widgets import Button, Static
9
10
11 class ConfirmationModal(ModalScreen[bool]):
12 """Modal screen for confirming destructive operations."""
13
14 BINDINGS = [
15 Binding("y", "confirm", "Yes", show=True),
16 Binding("n", "deny", "No", show=True),
17 Binding("escape", "deny", "Cancel", show=False),
18 ]
19
20 CSS = """
21 ConfirmationModal {
22 align: center middle;
23 }
24
25 #confirmation-dialog {
26 width: 60;
27 height: auto;
28 border: heavy $primary;
29 background: $surface;
30 padding: 1 2;
31 }
32
33 #confirmation-title {
34 text-style: bold;
35 color: $warning;
36 margin-bottom: 1;
37 }
38
39 #confirmation-message {
40 margin-bottom: 1;
41 }
42
43 #confirmation-details {
44 color: $text-muted;
45 margin-bottom: 1;
46 padding: 0 1;
47 border-left: solid $primary-lighten-2;
48 }
49
50 #confirmation-buttons {
51 align: center middle;
52 height: auto;
53 margin-top: 1;
54 }
55
56 #confirmation-buttons Button {
57 margin: 0 1;
58 }
59
60 #btn-yes {
61 background: $success;
62 }
63
64 #btn-no {
65 background: $error;
66 }
67 """
68
69 def __init__(
70 self,
71 tool_name: str,
72 message: str,
73 details: str = "",
74 **kwargs,
75 ) -> None:
76 super().__init__(**kwargs)
77 self.tool_name = tool_name
78 self.message = message
79 self.details = details
80
81 def compose(self) -> ComposeResult:
82 with Vertical(id="confirmation-dialog"):
83 yield Static(
84 Text(f"⚠ Confirm {self.tool_name}", style="bold yellow"),
85 id="confirmation-title",
86 markup=False,
87 )
88 yield Static(Text(self.message), id="confirmation-message", markup=False)
89 if self.details:
90 yield Static(
91 Text(self.details, style="dim"),
92 id="confirmation-details",
93 markup=False,
94 )
95 with Horizontal(id="confirmation-buttons"):
96 yield Button("Yes (y)", id="btn-yes", variant="success")
97 yield Button("No (n)", id="btn-no", variant="error")
98
99 def on_button_pressed(self, event: Button.Pressed) -> None:
100 """Handle button press."""
101 if event.button.id == "btn-yes":
102 self.dismiss(True)
103 else:
104 self.dismiss(False)
105
106 def action_confirm(self) -> None:
107 """Handle 'y' key."""
108 self.dismiss(True)
109
110 def action_deny(self) -> None:
111 """Handle 'n' or escape."""
112 self.dismiss(False)