superqode 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,476 @@
1
+ """
2
+ Enhanced Toast/Flash System - Rich Notifications.
3
+
4
+ Provides a comprehensive notification system with:
5
+ - Multiple toast types (success, warning, error, info, progress)
6
+ - Stacking and queuing
7
+ - Progress toasts with updates
8
+ - Interactive toasts with actions
9
+ - Auto-dismiss with configurable timeouts
10
+
11
+ Enhances the basic toast widget with production-ready features.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime
19
+ from enum import Enum
20
+ from typing import Callable, Dict, List, Optional
21
+ from uuid import uuid4
22
+
23
+ from rich.console import RenderableType
24
+ from rich.panel import Panel
25
+ from rich.progress import Progress, BarColumn, TextColumn
26
+ from rich.text import Text
27
+ from textual.reactive import reactive
28
+ from textual.widgets import Static
29
+ from textual.containers import Container
30
+ from textual.timer import Timer
31
+ from textual import events
32
+
33
+
34
+ class ToastType(Enum):
35
+ """Type of toast notification."""
36
+
37
+ SUCCESS = "success"
38
+ WARNING = "warning"
39
+ ERROR = "error"
40
+ INFO = "info"
41
+ PROGRESS = "progress"
42
+ ACTION = "action"
43
+
44
+
45
+ @dataclass
46
+ class ToastConfig:
47
+ """Configuration for toast behavior."""
48
+
49
+ # Timing
50
+ default_timeout: float = 5.0
51
+ error_timeout: float = 10.0
52
+ progress_timeout: float = 30.0
53
+
54
+ # Display
55
+ max_visible: int = 5
56
+ position: str = "bottom-right" # top-right, top-left, bottom-right, bottom-left
57
+ animation: bool = True
58
+
59
+ # Sound (if terminal supports)
60
+ sound_enabled: bool = False
61
+
62
+
63
+ @dataclass
64
+ class Toast:
65
+ """A single toast notification."""
66
+
67
+ id: str
68
+ type: ToastType
69
+ title: str
70
+ message: str = ""
71
+ timeout: float = 5.0
72
+ created_at: datetime = field(default_factory=datetime.now)
73
+
74
+ # Progress toast specific
75
+ progress: float = 0.0 # 0.0 to 1.0
76
+ progress_text: str = ""
77
+
78
+ # Action toast specific
79
+ actions: List[Dict] = field(default_factory=list) # [{"label": "...", "callback": ...}]
80
+
81
+ # State
82
+ dismissed: bool = False
83
+ pinned: bool = False # Don't auto-dismiss
84
+
85
+ @property
86
+ def is_expired(self) -> bool:
87
+ """Check if toast has expired."""
88
+ if self.pinned or self.dismissed:
89
+ return False
90
+ if self.type == ToastType.PROGRESS and self.progress < 1.0:
91
+ return False
92
+ elapsed = (datetime.now() - self.created_at).total_seconds()
93
+ return elapsed >= self.timeout
94
+
95
+
96
+ # Toast type styling
97
+ TOAST_STYLES = {
98
+ ToastType.SUCCESS: {
99
+ "icon": "✓",
100
+ "color": "#22c55e",
101
+ "border": "#16a34a",
102
+ "bg": "#052e16",
103
+ },
104
+ ToastType.WARNING: {
105
+ "icon": "⚠",
106
+ "color": "#eab308",
107
+ "border": "#ca8a04",
108
+ "bg": "#422006",
109
+ },
110
+ ToastType.ERROR: {
111
+ "icon": "✗",
112
+ "color": "#ef4444",
113
+ "border": "#dc2626",
114
+ "bg": "#450a0a",
115
+ },
116
+ ToastType.INFO: {
117
+ "icon": "ℹ",
118
+ "color": "#3b82f6",
119
+ "border": "#2563eb",
120
+ "bg": "#172554",
121
+ },
122
+ ToastType.PROGRESS: {
123
+ "icon": "◐",
124
+ "color": "#8b5cf6",
125
+ "border": "#7c3aed",
126
+ "bg": "#2e1065",
127
+ },
128
+ ToastType.ACTION: {
129
+ "icon": "▶",
130
+ "color": "#06b6d4",
131
+ "border": "#0891b2",
132
+ "bg": "#083344",
133
+ },
134
+ }
135
+
136
+
137
+ class ToastWidget(Static):
138
+ """Single toast notification widget."""
139
+
140
+ DEFAULT_CSS = """
141
+ ToastWidget {
142
+ width: 50;
143
+ height: auto;
144
+ margin: 0 0 1 0;
145
+ layer: notification;
146
+ }
147
+ """
148
+
149
+ def __init__(self, toast: Toast, on_dismiss: Callable[[], None], **kwargs):
150
+ super().__init__(**kwargs)
151
+ self.toast = toast
152
+ self._on_dismiss = on_dismiss
153
+ self._animation_frame = 0
154
+
155
+ def on_click(self, event: events.Click) -> None:
156
+ """Dismiss on click."""
157
+ if self.toast.type != ToastType.ACTION:
158
+ self._on_dismiss()
159
+
160
+ def on_key(self, event: events.Key) -> None:
161
+ """Handle key events for actions."""
162
+ if self.toast.type == ToastType.ACTION and self.toast.actions:
163
+ for i, action in enumerate(self.toast.actions):
164
+ if event.key == str(i + 1):
165
+ callback = action.get("callback")
166
+ if callback:
167
+ callback()
168
+ self._on_dismiss()
169
+ event.prevent_default()
170
+ return
171
+
172
+ def update_progress(self, progress: float, text: str = "") -> None:
173
+ """Update progress toast."""
174
+ self.toast.progress = progress
175
+ if text:
176
+ self.toast.progress_text = text
177
+ self.refresh()
178
+
179
+ def _get_spinner(self) -> str:
180
+ """Get spinner character for progress."""
181
+ spinners = ["◐", "◓", "◑", "◒"]
182
+ return spinners[self._animation_frame % len(spinners)]
183
+
184
+ def render(self) -> RenderableType:
185
+ """Render the toast."""
186
+ style = TOAST_STYLES.get(self.toast.type, TOAST_STYLES[ToastType.INFO])
187
+
188
+ content = Text()
189
+
190
+ # Icon
191
+ icon = style["icon"]
192
+ if self.toast.type == ToastType.PROGRESS and self.toast.progress < 1.0:
193
+ icon = self._get_spinner()
194
+
195
+ content.append(f" {icon} ", style=f"bold {style['color']}")
196
+
197
+ # Title
198
+ content.append(self.toast.title, style=f"bold {style['color']}")
199
+
200
+ # Message
201
+ if self.toast.message:
202
+ content.append(f"\n {self.toast.message}", style="#a1a1aa")
203
+
204
+ # Progress bar
205
+ if self.toast.type == ToastType.PROGRESS:
206
+ content.append("\n ")
207
+
208
+ bar_width = 30
209
+ filled = int(self.toast.progress * bar_width)
210
+
211
+ content.append("[", style="#3f3f46")
212
+ content.append("█" * filled, style=f"bold {style['color']}")
213
+ content.append("░" * (bar_width - filled), style="#27272a")
214
+ content.append("]", style="#3f3f46")
215
+
216
+ pct = int(self.toast.progress * 100)
217
+ content.append(f" {pct}%", style="#6b7280")
218
+
219
+ if self.toast.progress_text:
220
+ content.append(f"\n {self.toast.progress_text}", style="#6b7280")
221
+
222
+ # Actions
223
+ if self.toast.type == ToastType.ACTION and self.toast.actions:
224
+ content.append("\n ")
225
+ for i, action in enumerate(self.toast.actions):
226
+ content.append(f"[{i + 1}] ", style=f"bold {style['color']}")
227
+ content.append(f"{action['label']} ", style="#a1a1aa")
228
+
229
+ return Panel(
230
+ content,
231
+ border_style=style["border"],
232
+ padding=(0, 1),
233
+ )
234
+
235
+
236
+ class ToastContainer(Container):
237
+ """
238
+ Container for managing multiple toast notifications.
239
+
240
+ Usage:
241
+ # In your App
242
+ def compose(self):
243
+ yield ToastContainer(id="toasts")
244
+
245
+ # Show toasts
246
+ toasts = self.query_one("#toasts", ToastContainer)
247
+ toasts.success("File saved!")
248
+ toasts.error("Connection failed", "Could not reach server")
249
+
250
+ # Progress toast
251
+ toast_id = toasts.progress("Uploading...", "Starting upload")
252
+ toasts.update_progress(toast_id, 0.5, "50% complete")
253
+ toasts.update_progress(toast_id, 1.0, "Done!")
254
+ """
255
+
256
+ DEFAULT_CSS = """
257
+ ToastContainer {
258
+ dock: bottom;
259
+ width: 100%;
260
+ height: auto;
261
+ max-height: 50%;
262
+ align: right bottom;
263
+ padding: 1;
264
+ layer: notification;
265
+ }
266
+ """
267
+
268
+ def __init__(
269
+ self,
270
+ config: Optional[ToastConfig] = None,
271
+ **kwargs,
272
+ ):
273
+ super().__init__(**kwargs)
274
+ self.config = config or ToastConfig()
275
+ self._toasts: Dict[str, Toast] = {}
276
+ self._widgets: Dict[str, ToastWidget] = {}
277
+ self._timer: Optional[Timer] = None
278
+
279
+ def on_mount(self) -> None:
280
+ """Start cleanup timer."""
281
+ self._timer = self.set_interval(1.0, self._cleanup_expired)
282
+
283
+ def _cleanup_expired(self) -> None:
284
+ """Remove expired toasts."""
285
+ expired = [tid for tid, toast in self._toasts.items() if toast.is_expired]
286
+
287
+ for tid in expired:
288
+ self._remove_toast(tid)
289
+
290
+ def _add_toast(self, toast: Toast) -> str:
291
+ """Add a toast to the container."""
292
+ # Limit visible toasts
293
+ while len(self._toasts) >= self.config.max_visible:
294
+ oldest = min(self._toasts.values(), key=lambda t: t.created_at)
295
+ self._remove_toast(oldest.id)
296
+
297
+ self._toasts[toast.id] = toast
298
+
299
+ # Create widget
300
+ widget = ToastWidget(
301
+ toast,
302
+ on_dismiss=lambda: self._remove_toast(toast.id),
303
+ id=f"toast-{toast.id}",
304
+ )
305
+ self._widgets[toast.id] = widget
306
+ self.mount(widget)
307
+
308
+ return toast.id
309
+
310
+ def _remove_toast(self, toast_id: str) -> None:
311
+ """Remove a toast."""
312
+ toast = self._toasts.pop(toast_id, None)
313
+ widget = self._widgets.pop(toast_id, None)
314
+
315
+ if widget:
316
+ widget.remove()
317
+
318
+ if toast:
319
+ toast.dismissed = True
320
+
321
+ def success(self, title: str, message: str = "", timeout: float = None) -> str:
322
+ """Show a success toast."""
323
+ toast = Toast(
324
+ id=str(uuid4())[:8],
325
+ type=ToastType.SUCCESS,
326
+ title=title,
327
+ message=message,
328
+ timeout=timeout or self.config.default_timeout,
329
+ )
330
+ return self._add_toast(toast)
331
+
332
+ def warning(self, title: str, message: str = "", timeout: float = None) -> str:
333
+ """Show a warning toast."""
334
+ toast = Toast(
335
+ id=str(uuid4())[:8],
336
+ type=ToastType.WARNING,
337
+ title=title,
338
+ message=message,
339
+ timeout=timeout or self.config.default_timeout,
340
+ )
341
+ return self._add_toast(toast)
342
+
343
+ def error(self, title: str, message: str = "", timeout: float = None) -> str:
344
+ """Show an error toast."""
345
+ toast = Toast(
346
+ id=str(uuid4())[:8],
347
+ type=ToastType.ERROR,
348
+ title=title,
349
+ message=message,
350
+ timeout=timeout or self.config.error_timeout,
351
+ )
352
+ return self._add_toast(toast)
353
+
354
+ def info(self, title: str, message: str = "", timeout: float = None) -> str:
355
+ """Show an info toast."""
356
+ toast = Toast(
357
+ id=str(uuid4())[:8],
358
+ type=ToastType.INFO,
359
+ title=title,
360
+ message=message,
361
+ timeout=timeout or self.config.default_timeout,
362
+ )
363
+ return self._add_toast(toast)
364
+
365
+ def progress(
366
+ self,
367
+ title: str,
368
+ message: str = "",
369
+ initial_progress: float = 0.0,
370
+ ) -> str:
371
+ """Show a progress toast."""
372
+ toast = Toast(
373
+ id=str(uuid4())[:8],
374
+ type=ToastType.PROGRESS,
375
+ title=title,
376
+ message=message,
377
+ progress=initial_progress,
378
+ timeout=self.config.progress_timeout,
379
+ pinned=True,
380
+ )
381
+ return self._add_toast(toast)
382
+
383
+ def update_progress(
384
+ self,
385
+ toast_id: str,
386
+ progress: float,
387
+ text: str = "",
388
+ ) -> None:
389
+ """Update a progress toast."""
390
+ toast = self._toasts.get(toast_id)
391
+ if toast and toast.type == ToastType.PROGRESS:
392
+ toast.progress = progress
393
+ if text:
394
+ toast.progress_text = text
395
+
396
+ # Auto-dismiss when complete
397
+ if progress >= 1.0:
398
+ toast.pinned = False
399
+ toast.timeout = 3.0
400
+ toast.created_at = datetime.now()
401
+
402
+ widget = self._widgets.get(toast_id)
403
+ if widget:
404
+ widget.refresh()
405
+
406
+ def action(
407
+ self,
408
+ title: str,
409
+ message: str = "",
410
+ actions: List[Dict] = None,
411
+ ) -> str:
412
+ """Show an action toast with buttons."""
413
+ toast = Toast(
414
+ id=str(uuid4())[:8],
415
+ type=ToastType.ACTION,
416
+ title=title,
417
+ message=message,
418
+ actions=actions or [],
419
+ pinned=True, # Don't auto-dismiss action toasts
420
+ timeout=30.0,
421
+ )
422
+ return self._add_toast(toast)
423
+
424
+ def dismiss(self, toast_id: str) -> None:
425
+ """Manually dismiss a toast."""
426
+ self._remove_toast(toast_id)
427
+
428
+ def dismiss_all(self) -> None:
429
+ """Dismiss all toasts."""
430
+ for toast_id in list(self._toasts.keys()):
431
+ self._remove_toast(toast_id)
432
+
433
+
434
+ # Convenience functions for module-level usage
435
+ _global_container: Optional[ToastContainer] = None
436
+
437
+
438
+ def set_toast_container(container: ToastContainer) -> None:
439
+ """Set the global toast container."""
440
+ global _global_container
441
+ _global_container = container
442
+
443
+
444
+ def toast_success(title: str, message: str = "") -> Optional[str]:
445
+ """Show a success toast."""
446
+ if _global_container:
447
+ return _global_container.success(title, message)
448
+ return None
449
+
450
+
451
+ def toast_warning(title: str, message: str = "") -> Optional[str]:
452
+ """Show a warning toast."""
453
+ if _global_container:
454
+ return _global_container.warning(title, message)
455
+ return None
456
+
457
+
458
+ def toast_error(title: str, message: str = "") -> Optional[str]:
459
+ """Show an error toast."""
460
+ if _global_container:
461
+ return _global_container.error(title, message)
462
+ return None
463
+
464
+
465
+ def toast_info(title: str, message: str = "") -> Optional[str]:
466
+ """Show an info toast."""
467
+ if _global_container:
468
+ return _global_container.info(title, message)
469
+ return None
470
+
471
+
472
+ def toast_progress(title: str, message: str = "") -> Optional[str]:
473
+ """Show a progress toast."""
474
+ if _global_container:
475
+ return _global_container.progress(title, message)
476
+ return None