cct-cli 0.7.9.0__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 (325) hide show
  1. calc_terminal/__init__.py +14 -0
  2. calc_terminal/__main__.py +14 -0
  3. calc_terminal/activity.py +1334 -0
  4. calc_terminal/agent.py +3387 -0
  5. calc_terminal/agent_runtime.py +519 -0
  6. calc_terminal/ai_context.py +447 -0
  7. calc_terminal/ai_modes.py +752 -0
  8. calc_terminal/ai_personalization.py +286 -0
  9. calc_terminal/ai_preview_feedback.py +213 -0
  10. calc_terminal/aicore.py +2572 -0
  11. calc_terminal/anim.py +367 -0
  12. calc_terminal/app.py +3685 -0
  13. calc_terminal/art.py +639 -0
  14. calc_terminal/atomsim.py +368 -0
  15. calc_terminal/attachments.py +743 -0
  16. calc_terminal/benchmark_system.py +414 -0
  17. calc_terminal/browser/__init__.py +36 -0
  18. calc_terminal/browser/browser_state.py +346 -0
  19. calc_terminal/browser/devserver.py +176 -0
  20. calc_terminal/browser/engine.py +494 -0
  21. calc_terminal/browser/navigation.py +84 -0
  22. calc_terminal/browser/preview.py +429 -0
  23. calc_terminal/browser/preview_entry.py +95 -0
  24. calc_terminal/browser/project_detector.py +144 -0
  25. calc_terminal/browser/server.py +449 -0
  26. calc_terminal/browser/state.py +75 -0
  27. calc_terminal/browser/watcher.py +99 -0
  28. calc_terminal/browser_gui/__init__.py +1 -0
  29. calc_terminal/browser_gui/__main__.py +3 -0
  30. calc_terminal/browser_gui/launcher.py +173 -0
  31. calc_terminal/browser_gui/playwright_browser.py +117 -0
  32. calc_terminal/browser_gui/qt_browser.py +1501 -0
  33. calc_terminal/browser_gui/webview_browser.py +57 -0
  34. calc_terminal/capabilities/__init__.py +35 -0
  35. calc_terminal/capabilities/adapters/__init__.py +33 -0
  36. calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
  37. calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
  38. calc_terminal/capabilities/adapters/filesystem.py +206 -0
  39. calc_terminal/capabilities/adapters/git_adapter.py +202 -0
  40. calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
  41. calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
  42. calc_terminal/capabilities/adapters/platforms.py +200 -0
  43. calc_terminal/capabilities/adapters/python_exec.py +93 -0
  44. calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
  45. calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
  46. calc_terminal/capabilities/adapters/structural_bio.py +161 -0
  47. calc_terminal/capabilities/adapters/terminal.py +99 -0
  48. calc_terminal/capabilities/bus.py +178 -0
  49. calc_terminal/capabilities/discovery.py +207 -0
  50. calc_terminal/capabilities/schema.py +221 -0
  51. calc_terminal/cat.ico +0 -0
  52. calc_terminal/cat_browser.py +2018 -0
  53. calc_terminal/chat_store.py +703 -0
  54. calc_terminal/cli.py +1178 -0
  55. calc_terminal/code_editor.py +640 -0
  56. calc_terminal/collaboration.py +723 -0
  57. calc_terminal/commands_data.py +139 -0
  58. calc_terminal/compatibility_engine.py +352 -0
  59. calc_terminal/compute/__init__.py +31 -0
  60. calc_terminal/compute/fabric.py +350 -0
  61. calc_terminal/config.py +227 -0
  62. calc_terminal/core/__init__.py +41 -0
  63. calc_terminal/core/checkpoint.py +156 -0
  64. calc_terminal/core/input/__init__.py +45 -0
  65. calc_terminal/core/mode_registry.py +300 -0
  66. calc_terminal/core/project_graph.py +172 -0
  67. calc_terminal/core/recovery.py +129 -0
  68. calc_terminal/core/security_layer.py +112 -0
  69. calc_terminal/core/task_graph.py +202 -0
  70. calc_terminal/core/unified_runtime.py +184 -0
  71. calc_terminal/core/verification.py +257 -0
  72. calc_terminal/customization.py +1566 -0
  73. calc_terminal/derivations.py +153 -0
  74. calc_terminal/device_control.py +263 -0
  75. calc_terminal/diagnostics/__init__.py +27 -0
  76. calc_terminal/diagnostics/doctor_engine.py +382 -0
  77. calc_terminal/diagnostics/self_test.py +247 -0
  78. calc_terminal/doctor.py +519 -0
  79. calc_terminal/easter_eggs.py +274 -0
  80. calc_terminal/editor/__init__.py +1 -0
  81. calc_terminal/editor/actions.py +263 -0
  82. calc_terminal/editor/commands.py +160 -0
  83. calc_terminal/editor/shortcuts.py +226 -0
  84. calc_terminal/engine.py +259 -0
  85. calc_terminal/errors.py +120 -0
  86. calc_terminal/event_stream.py +146 -0
  87. calc_terminal/eventbus.py +133 -0
  88. calc_terminal/extensions.py +733 -0
  89. calc_terminal/fallback_cli.py +1321 -0
  90. calc_terminal/first_run.py +265 -0
  91. calc_terminal/fomoji_auth.py +1043 -0
  92. calc_terminal/formulas.py +82 -0
  93. calc_terminal/fs_cache.py +121 -0
  94. calc_terminal/fs_watcher.py +277 -0
  95. calc_terminal/game.py +193 -0
  96. calc_terminal/gen1.py +5 -0
  97. calc_terminal/generators.py +245 -0
  98. calc_terminal/gestures/__init__.py +42 -0
  99. calc_terminal/gestures/bindings.py +175 -0
  100. calc_terminal/gestures/manager.py +477 -0
  101. calc_terminal/goodbye.py +363 -0
  102. calc_terminal/gpu3d.py +290 -0
  103. calc_terminal/graphs.py +358 -0
  104. calc_terminal/hardware_analyzer.py +440 -0
  105. calc_terminal/host/__init__.py +30 -0
  106. calc_terminal/host/browser_manager.py +187 -0
  107. calc_terminal/host/desktop.py +1386 -0
  108. calc_terminal/host/launcher.py +395 -0
  109. calc_terminal/host/terminal.py +279 -0
  110. calc_terminal/identity.py +216 -0
  111. calc_terminal/input/__init__.py +54 -0
  112. calc_terminal/input/capabilities.py +258 -0
  113. calc_terminal/input/focus.py +87 -0
  114. calc_terminal/input/gestures.py +64 -0
  115. calc_terminal/input/pointer.py +114 -0
  116. calc_terminal/input/touch.py +345 -0
  117. calc_terminal/keys.py +84 -0
  118. calc_terminal/live_automation.py +165 -0
  119. calc_terminal/mathtext.py +433 -0
  120. calc_terminal/mcp.py +386 -0
  121. calc_terminal/memory.py +337 -0
  122. calc_terminal/memory_v2.py +479 -0
  123. calc_terminal/metrics.py +333 -0
  124. calc_terminal/mode_detection.py +146 -0
  125. calc_terminal/model.py +2431 -0
  126. calc_terminal/model_router.py +665 -0
  127. calc_terminal/models/__init__.py +0 -0
  128. calc_terminal/models/active_state.py +187 -0
  129. calc_terminal/models/dynamic_registry.py +584 -0
  130. calc_terminal/models/manager.py +781 -0
  131. calc_terminal/models/model_metadata.json +3526 -0
  132. calc_terminal/models/profiles.py +194 -0
  133. calc_terminal/models/registry.py +265 -0
  134. calc_terminal/models/schema.py +197 -0
  135. calc_terminal/models/validator.py +287 -0
  136. calc_terminal/models/verification_engine.py +368 -0
  137. calc_terminal/native_picker.py +215 -0
  138. calc_terminal/ollama_catalog.py +279 -0
  139. calc_terminal/ollama_download.py +233 -0
  140. calc_terminal/orchestrator.py +304 -0
  141. calc_terminal/package_research.py +322 -0
  142. calc_terminal/packages.py +1024 -0
  143. calc_terminal/pc_specs.py +116 -0
  144. calc_terminal/permissions.py +334 -0
  145. calc_terminal/pet.py +106 -0
  146. calc_terminal/pipeline.py +505 -0
  147. calc_terminal/platform/__init__.py +491 -0
  148. calc_terminal/platform/desktop.py +491 -0
  149. calc_terminal/platform/web.py +781 -0
  150. calc_terminal/preview/__init__.py +1 -0
  151. calc_terminal/preview/dev_server.py +303 -0
  152. calc_terminal/preview/diagnostics.py +131 -0
  153. calc_terminal/preview/live_reload.py +66 -0
  154. calc_terminal/preview/manager.py +129 -0
  155. calc_terminal/project_stats.py +209 -0
  156. calc_terminal/projects.py +328 -0
  157. calc_terminal/providers/__init__.py +0 -0
  158. calc_terminal/providers/adapters/__init__.py +80 -0
  159. calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
  160. calc_terminal/providers/adapters/base.py +106 -0
  161. calc_terminal/providers/adapters/chinese_adapters.py +420 -0
  162. calc_terminal/providers/adapters/gemini_adapter.py +101 -0
  163. calc_terminal/providers/adapters/ollama_adapter.py +83 -0
  164. calc_terminal/providers/adapters/openai_adapter.py +159 -0
  165. calc_terminal/providers/adapters/other_adapters.py +246 -0
  166. calc_terminal/providers/anthropic_provider.py +172 -0
  167. calc_terminal/providers/auto_update.py +416 -0
  168. calc_terminal/providers/base_provider.py +105 -0
  169. calc_terminal/providers/discovery_manager.py +207 -0
  170. calc_terminal/providers/gemini_provider.py +178 -0
  171. calc_terminal/providers/lifecycle.py +767 -0
  172. calc_terminal/providers/ollama_adapter.py +707 -0
  173. calc_terminal/providers/openai_provider.py +254 -0
  174. calc_terminal/providers/provider_manager.py +1827 -0
  175. calc_terminal/providers/providers.json +4075 -0
  176. calc_terminal/reactionsim.py +279 -0
  177. calc_terminal/registry.py +337 -0
  178. calc_terminal/report.py +162 -0
  179. calc_terminal/research/__init__.py +45 -0
  180. calc_terminal/research/artifact_intel.py +126 -0
  181. calc_terminal/research/data_lineage.py +123 -0
  182. calc_terminal/research/experiment_ledger.py +303 -0
  183. calc_terminal/research/reproducibility.py +131 -0
  184. calc_terminal/resilience/__init__.py +47 -0
  185. calc_terminal/resilience/agent_state.py +121 -0
  186. calc_terminal/resilience/capability_matcher.py +174 -0
  187. calc_terminal/resilience/circuit_breaker.py +158 -0
  188. calc_terminal/resilience/failover_engine.py +230 -0
  189. calc_terminal/resilience/health_monitor.py +192 -0
  190. calc_terminal/resilience/ollama_adapter.py +125 -0
  191. calc_terminal/resilience/orchestrator.py +312 -0
  192. calc_terminal/resilience/types.py +134 -0
  193. calc_terminal/sandbox.py +98 -0
  194. calc_terminal/scires.py +558 -0
  195. calc_terminal/security_scanner.py +126 -0
  196. calc_terminal/session.py +294 -0
  197. calc_terminal/sim3d.py +206 -0
  198. calc_terminal/solver.py +276 -0
  199. calc_terminal/sound.py +127 -0
  200. calc_terminal/task_reports.py +287 -0
  201. calc_terminal/terminal_host.py +201 -0
  202. calc_terminal/terminal_identity.py +411 -0
  203. calc_terminal/test_ai_mode_reliability.py +344 -0
  204. calc_terminal/test_browser.py +368 -0
  205. calc_terminal/test_code_editor_upgrade.py +485 -0
  206. calc_terminal/test_customization.py +1148 -0
  207. calc_terminal/test_customization_ui.py +612 -0
  208. calc_terminal/test_dynamic_registry.py +304 -0
  209. calc_terminal/test_extensions.py +436 -0
  210. calc_terminal/test_overhaul.py +557 -0
  211. calc_terminal/test_project_detect.py +255 -0
  212. calc_terminal/test_root_cause_fix.py +527 -0
  213. calc_terminal/test_stability.py +532 -0
  214. calc_terminal/test_terminal_identity.py +132 -0
  215. calc_terminal/test_v079_speed.py +460 -0
  216. calc_terminal/theme.py +1107 -0
  217. calc_terminal/timeline.py +139 -0
  218. calc_terminal/todos.py +246 -0
  219. calc_terminal/tool_call_normalizer.py +419 -0
  220. calc_terminal/tui.py +104 -0
  221. calc_terminal/ui/__init__.py +8 -0
  222. calc_terminal/ui/activity_panel.py +231 -0
  223. calc_terminal/ui/activity_stream_panel.py +238 -0
  224. calc_terminal/ui/animations.py +122 -0
  225. calc_terminal/ui/app.py +7271 -0
  226. calc_terminal/ui/attach_panel.py +597 -0
  227. calc_terminal/ui/attachments.py +424 -0
  228. calc_terminal/ui/backup_panel.py +810 -0
  229. calc_terminal/ui/browser_shell.py +887 -0
  230. calc_terminal/ui/cat_agent.py +357 -0
  231. calc_terminal/ui/chats_panel.py +899 -0
  232. calc_terminal/ui/command_palette.py +125 -0
  233. calc_terminal/ui/command_palette_modal.py +166 -0
  234. calc_terminal/ui/composer.py +1141 -0
  235. calc_terminal/ui/context_menu.py +197 -0
  236. calc_terminal/ui/conversation.py +1435 -0
  237. calc_terminal/ui/customization_panel.py +1229 -0
  238. calc_terminal/ui/dashboard.py +404 -0
  239. calc_terminal/ui/design_system.py +557 -0
  240. calc_terminal/ui/diff_panel.py +213 -0
  241. calc_terminal/ui/editor.py +2102 -0
  242. calc_terminal/ui/empty_state.py +302 -0
  243. calc_terminal/ui/events.py +487 -0
  244. calc_terminal/ui/extensions_panel.py +815 -0
  245. calc_terminal/ui/footer.py +166 -0
  246. calc_terminal/ui/gestures_panel.py +383 -0
  247. calc_terminal/ui/goodbye_screen.py +100 -0
  248. calc_terminal/ui/header.py +1034 -0
  249. calc_terminal/ui/help_panel.py +254 -0
  250. calc_terminal/ui/live_activities.py +914 -0
  251. calc_terminal/ui/mcp_panel.py +570 -0
  252. calc_terminal/ui/memory_center.py +524 -0
  253. calc_terminal/ui/mode_colors_panel.py +525 -0
  254. calc_terminal/ui/nav_screens.py +747 -0
  255. calc_terminal/ui/ollama_panel.py +536 -0
  256. calc_terminal/ui/palette.py +221 -0
  257. calc_terminal/ui/permission_panel.py +269 -0
  258. calc_terminal/ui/personalization_panel.py +517 -0
  259. calc_terminal/ui/personalize_center.py +1568 -0
  260. calc_terminal/ui/preview_panel.py +441 -0
  261. calc_terminal/ui/resizers.py +402 -0
  262. calc_terminal/ui/sidebar.py +1285 -0
  263. calc_terminal/ui/statusbar.py +168 -0
  264. calc_terminal/ui/theme_css.py +1396 -0
  265. calc_terminal/ui/thinking.py +226 -0
  266. calc_terminal/ui/timeline_panel.py +102 -0
  267. calc_terminal/ui/todo_panel.py +193 -0
  268. calc_terminal/ui/viewport.py +136 -0
  269. calc_terminal/ui/vision_panel.py +489 -0
  270. calc_terminal/ui/welcome_modal.py +343 -0
  271. calc_terminal/ui/widgets.py +160 -0
  272. calc_terminal/ui/workspace.py +831 -0
  273. calc_terminal/viewers/__init__.py +1 -0
  274. calc_terminal/viewers/document_viewer.py +252 -0
  275. calc_terminal/viewers/image_viewer.py +241 -0
  276. calc_terminal/viewers/pdf_viewer.py +203 -0
  277. calc_terminal/viewers/presentation_viewer.py +164 -0
  278. calc_terminal/viewers/registry.py +120 -0
  279. calc_terminal/viewers/spreadsheet_viewer.py +204 -0
  280. calc_terminal/vision/__init__.py +89 -0
  281. calc_terminal/vision/analysis.py +194 -0
  282. calc_terminal/vision/annotations.py +297 -0
  283. calc_terminal/vision/capture.py +171 -0
  284. calc_terminal/vision/context.py +231 -0
  285. calc_terminal/vision/correlation.py +169 -0
  286. calc_terminal/vision/cursor.py +258 -0
  287. calc_terminal/vision/events.py +66 -0
  288. calc_terminal/vision/frame_pipeline.py +259 -0
  289. calc_terminal/vision/priority.py +218 -0
  290. calc_terminal/vision/provider.py +180 -0
  291. calc_terminal/vision/safety.py +149 -0
  292. calc_terminal/vision/session.py +281 -0
  293. calc_terminal/vision/verify.py +162 -0
  294. calc_terminal/vision.py +514 -0
  295. calc_terminal/vscode_integration.py +113 -0
  296. calc_terminal/web/__init__.py +8 -0
  297. calc_terminal/web/cat_runtime.py +710 -0
  298. calc_terminal/web/server.py +2891 -0
  299. calc_terminal/web/static/css/app.css +3152 -0
  300. calc_terminal/web/static/icons/badge-72.png +0 -0
  301. calc_terminal/web/static/icons/cat.ico +0 -0
  302. calc_terminal/web/static/icons/icon-128.png +0 -0
  303. calc_terminal/web/static/icons/icon-144.png +0 -0
  304. calc_terminal/web/static/icons/icon-152.png +0 -0
  305. calc_terminal/web/static/icons/icon-192.png +0 -0
  306. calc_terminal/web/static/icons/icon-384.png +0 -0
  307. calc_terminal/web/static/icons/icon-512.png +0 -0
  308. calc_terminal/web/static/icons/icon-72.png +0 -0
  309. calc_terminal/web/static/icons/icon-96.png +0 -0
  310. calc_terminal/web/static/icons/icon.svg +34 -0
  311. calc_terminal/web/static/icons/new-project.png +0 -0
  312. calc_terminal/web/static/icons/open-project.png +0 -0
  313. calc_terminal/web/static/index.html +734 -0
  314. calc_terminal/web/static/js/app.js +2403 -0
  315. calc_terminal/web/static/manifest.json +88 -0
  316. calc_terminal/web/static/sw.js +230 -0
  317. calc_terminal/workflow_engine.py +769 -0
  318. calc_terminal/workspace.py +593 -0
  319. calc_terminal/workspace_index.py +385 -0
  320. cct_cli-0.7.9.0.dist-info/METADATA +210 -0
  321. cct_cli-0.7.9.0.dist-info/RECORD +325 -0
  322. cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
  323. cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
  324. cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
  325. cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,487 @@
1
+ """
2
+ CCT UI events — the public contract between UI components.
3
+
4
+ Components never call each other directly (ConversationView never
5
+ calls a Composer method; Composer never reaches into ConversationView;
6
+ PermissionCard never touches ConversationView). Everything crosses
7
+ component boundaries as one of these Message subclasses, posted with
8
+ `self.post_message(...)` and handled by a parent's `on_<event_name>`
9
+ method (Textual's normal message-bubbling convention) or centrally in
10
+ calc_terminal/ui/app.py's CCTApp, which is the only place allowed to
11
+ coordinate more than one component at once.
12
+
13
+ Every event here is a plain data carrier: attributes only, no methods
14
+ that do work. Deciding what an event *means* (allow this action, call
15
+ the AI, switch the theme) is business logic and lives outside ui/.
16
+ """
17
+
18
+ if __name__ == "__main__":
19
+ print("This is a library file and is not meant to be run directly.")
20
+ import sys
21
+ sys.exit(1)
22
+
23
+ TEXTUAL_AVAILABLE = True
24
+ try:
25
+ from textual.message import Message
26
+ except Exception:
27
+ TEXTUAL_AVAILABLE = False
28
+
29
+ class Message: # minimal stand-in so `class X(Message)` below still
30
+ """Textual isn't installed — these event classes still need to
31
+ exist so `from .events import X` doesn't raise ImportError
32
+ before the caller gets a chance to check TEXTUAL_AVAILABLE."""
33
+
34
+ def __init__(self):
35
+ pass
36
+
37
+
38
+ # ------------------------------------------------------------- messaging --
39
+ class MessageSubmitted(Message):
40
+ """Composer -> app. The user submitted the composer (Send / Ctrl+Enter)."""
41
+
42
+ def __init__(self, text, attachments=None):
43
+ self.text = text
44
+ self.attachments = attachments or []
45
+ super().__init__()
46
+
47
+
48
+ class MessageStarted(Message):
49
+ """App -> conversation. A new turn is being created for `turn_id`.
50
+ `prompt` is the text that triggered it — optional, and only used by
51
+ the composer's status bar for smart stage detection (thinking.py),
52
+ so existing callers that don't pass it keep working unchanged."""
53
+
54
+ def __init__(self, turn_id, role="assistant", prompt=""):
55
+ self.turn_id = turn_id
56
+ self.role = role
57
+ self.prompt = prompt
58
+ super().__init__()
59
+
60
+
61
+ class MessageChunk(Message):
62
+ """App -> conversation. One streamed token/fragment for `turn_id`."""
63
+
64
+ def __init__(self, turn_id, text):
65
+ self.turn_id = turn_id
66
+ self.text = text
67
+ super().__init__()
68
+
69
+
70
+ class MessageFinished(Message):
71
+ """App -> conversation. `turn_id` is complete; `full_text` is final."""
72
+
73
+ def __init__(self, turn_id, full_text, duration=0.0, usage=None):
74
+ self.turn_id = turn_id
75
+ self.full_text = full_text
76
+ self.duration = duration
77
+ self.usage = usage or {}
78
+ super().__init__()
79
+
80
+
81
+ # ------------------------------------------------------------- streaming --
82
+ class StreamingStarted(Message):
83
+ """Brackets a streaming session (drives the footer's live indicator)."""
84
+
85
+ def __init__(self, turn_id):
86
+ self.turn_id = turn_id
87
+ super().__init__()
88
+
89
+
90
+ class StreamingFinished(Message):
91
+ def __init__(self, turn_id):
92
+ self.turn_id = turn_id
93
+ super().__init__()
94
+
95
+
96
+ class AgentActivity(Message):
97
+ """App -> conversation (v0.7.9.0): a real backend agent-state
98
+ crossing for the CAT Agent ASCII indicator. `state` is one of:
99
+
100
+ thinking model request in flight / processing results
101
+ tool_execution an agent tool is running
102
+ waiting_permission blocked on the user's permission decision
103
+
104
+ The indicator starts with MessageStarted (thinking), may flip to
105
+ tool_execution/waiting_permission and back while the agent loop
106
+ runs, and is always removed by MessageFinished — which covers the
107
+ success, error AND cancelled exits, because every one of those
108
+ paths still posts MessageFinished (partial text kept on cancel).
109
+ This is deliberately a UI-only carrier: it never rides along in
110
+ model requests and never becomes conversation content."""
111
+
112
+ STATES = ("thinking", "tool_execution", "tool_running",
113
+ "waiting_permission")
114
+
115
+ def __init__(self, turn_id, state="thinking"):
116
+ self.turn_id = turn_id
117
+ self.state = state if state in self.STATES else "thinking"
118
+ super().__init__()
119
+
120
+
121
+ # ------------------------------------------------------------ permissions --
122
+ class PermissionRequested(Message):
123
+ """Backend -> app. `request_id` is a one-shot token the eventual
124
+ PermissionGranted/Denied must echo back so the right pending call
125
+ resolves (see calc_terminal/permissions.py's manager.decide)."""
126
+
127
+ def __init__(self, request_id, key, action_text, reason):
128
+ self.request_id = request_id
129
+ self.key = key
130
+ self.action_text = action_text
131
+ self.reason = reason
132
+ super().__init__()
133
+
134
+
135
+ class PermissionGranted(Message):
136
+ def __init__(self, request_id, key, remember=False):
137
+ self.request_id = request_id
138
+ self.key = key
139
+ self.remember = remember
140
+ super().__init__()
141
+
142
+
143
+ class PermissionDenied(Message):
144
+ """`remember=True` means Always Deny (v0.7.7 spec section 3's
145
+ dialog offers Deny / Always Deny / Cancel on top of the existing
146
+ Allow Once / Always Allow) — the denied key is recorded so future
147
+ requests are refused without prompting at all."""
148
+
149
+ def __init__(self, request_id, key, remember=False):
150
+ self.request_id = request_id
151
+ self.key = key
152
+ self.remember = remember
153
+ super().__init__()
154
+
155
+
156
+ class PermissionCancelled(Message):
157
+ """The user picked Cancel instead of Allow/Deny — the pending call
158
+ is resolved as a cancellation (nothing ran, nothing was recorded as
159
+ denied)."""
160
+
161
+ def __init__(self, request_id, key):
162
+ self.request_id = request_id
163
+ self.key = key
164
+ super().__init__()
165
+
166
+
167
+ # ------------------------------------------------------------ attachments --
168
+ class AttachmentAdded(Message):
169
+ def __init__(self, chip_id, label, kind="file"):
170
+ self.chip_id = chip_id
171
+ self.label = label
172
+ self.kind = kind # "file" | "paste" | "image"
173
+ super().__init__()
174
+
175
+
176
+ class AttachmentRemoved(Message):
177
+ def __init__(self, chip_id):
178
+ self.chip_id = chip_id
179
+ super().__init__()
180
+
181
+
182
+ class PasteCollapsed(Message):
183
+ def __init__(self, chip_id, line_count):
184
+ self.chip_id = chip_id
185
+ self.line_count = line_count
186
+ super().__init__()
187
+
188
+
189
+ class PasteExpanded(Message):
190
+ def __init__(self, chip_id, text):
191
+ self.chip_id = chip_id
192
+ self.text = text
193
+ super().__init__()
194
+
195
+
196
+ class AttachmentChipDoubleClicked(Message):
197
+ """AttachmentBar -> app. An attachment chip's label was double-
198
+ clicked. For .txt attachments this means "import the text into the
199
+ chat now" (requirement #18); single-click stays normal behavior."""
200
+
201
+ def __init__(self, chip_id, path, name):
202
+ self.chip_id = chip_id
203
+ self.path = path
204
+ self.name = name
205
+ super().__init__()
206
+
207
+
208
+ # ----------------------------------------------------------- commands/etc --
209
+ class CommandExecuted(Message):
210
+ def __init__(self, command, args=""):
211
+ self.command = command
212
+ self.args = args
213
+ super().__init__()
214
+
215
+
216
+ class ConversationLoaded(Message):
217
+ def __init__(self, turn_count):
218
+ self.turn_count = turn_count
219
+ super().__init__()
220
+
221
+
222
+ class ConversationSaved(Message):
223
+ def __init__(self, path):
224
+ self.path = path
225
+ super().__init__()
226
+
227
+
228
+ class ConversationCleared(Message):
229
+ def __init__(self):
230
+ super().__init__()
231
+
232
+
233
+ class NotebookChanged(Message):
234
+ def __init__(self, mode):
235
+ self.mode = mode
236
+ super().__init__()
237
+
238
+
239
+ class WorkspaceChanged(Message):
240
+ def __init__(self, workspace):
241
+ self.workspace = workspace
242
+ super().__init__()
243
+
244
+
245
+ class ModelChanged(Message):
246
+ def __init__(self, model_name):
247
+ self.model_name = model_name
248
+ super().__init__()
249
+
250
+
251
+ class ThemeChanged(Message):
252
+ def __init__(self, theme_name):
253
+ self.theme_name = theme_name
254
+ super().__init__()
255
+
256
+
257
+ class ErrorOccurred(Message):
258
+ def __init__(self, message, detail=""):
259
+ self.message = message
260
+ self.detail = detail
261
+ super().__init__()
262
+
263
+
264
+ class RetryRequested(Message):
265
+ def __init__(self, turn_id):
266
+ self.turn_id = turn_id
267
+ super().__init__()
268
+
269
+
270
+ class SessionEnded(Message):
271
+ def __init__(self):
272
+ super().__init__()
273
+
274
+
275
+ # ------------------------------------------------------- v0.7 IDE events --
276
+ class PermissionModeChanged(Message):
277
+ """HeaderPermissionSelector -> app. `mode` is one of
278
+ permissions.MODES ('ask' | 'restricted' | 'full')."""
279
+
280
+ def __init__(self, mode):
281
+ self.mode = mode
282
+ super().__init__()
283
+
284
+
285
+ class FolderOpened(Message):
286
+ """Sidebar/app -> app. A project folder was opened as the IDE
287
+ workspace root (spec section 16's "Open Folder")."""
288
+
289
+ def __init__(self, path):
290
+ self.path = path
291
+ super().__init__()
292
+
293
+
294
+ class SidebarToggled(Message):
295
+ def __init__(self, visible):
296
+ self.visible = visible
297
+ super().__init__()
298
+
299
+
300
+ class FileOpenRequested(Message):
301
+ """Sidebar (or drag & drop) -> app: open this path in the editor."""
302
+
303
+ def __init__(self, path):
304
+ self.path = path
305
+ super().__init__()
306
+
307
+
308
+ class FileSaved(Message):
309
+ def __init__(self, path):
310
+ self.path = path
311
+ super().__init__()
312
+
313
+
314
+ class MessageRewritten(Message):
315
+ """StickyComposer -> app: posted instead of MessageSubmitted when
316
+ the composer was in Rewrite edit mode (see
317
+ StickyComposer.start_edit, triggered by the context menu's
318
+ 'rewrite' action) and the user pressed Enter to save.
319
+
320
+ Deliberately a separate event rather than an optional field on
321
+ MessageSubmitted: a plain submit always APPENDS a new user turn
322
+ and a new assistant turn (on_message_submitted), while this always
323
+ REPLACES the existing `turn_id`'s text and regenerates its paired
324
+ assistant reply in place (on_message_rewritten /
325
+ CCTApp._regenerate_turn) — different enough handling that
326
+ overloading one event with an if-branch would just move the
327
+ duplicate-vs-replace bug this fixes from the UI into the event
328
+ handler instead of removing it."""
329
+
330
+ def __init__(self, turn_id, text, attachments=None):
331
+ self.turn_id = turn_id
332
+ self.text = text
333
+ self.attachments = attachments or []
334
+ super().__init__()
335
+
336
+
337
+ class MessageContextAction(Message):
338
+ """ConversationItem (via context_menu.MessageContextMenu) -> app.
339
+ `action` is one of: 'new_session', 'retry:<mode>' (mode is one of
340
+ ai_modes.MODE_ORDER), 'rewrite', 'revert', 'fork' — 'copy' is
341
+ deliberately NOT posted here: it needs no cross-component
342
+ coordination (clipboard + a local confirmation flash), so
343
+ ConversationItem handles it entirely itself and never posts this
344
+ for it. `turn_id` is the turn the menu was opened on."""
345
+
346
+ def __init__(self, action, turn_id):
347
+ self.action = action
348
+ self.turn_id = turn_id
349
+ super().__init__()
350
+
351
+
352
+ class NavAction(Message):
353
+ """NavPanel (opened from the header's menu button) -> app. `action`
354
+ is one of the panel's item ids: 'dashboard', 'open_folder',
355
+ 'recent_workspaces', 'mcp_servers', 'customize_ai', 'diff_viewer',
356
+ 'settings', 'themes', 'extensions', 'keyboard_shortcuts', 'help'.
357
+ Deliberately a single generic event (like MessageContextAction)
358
+ rather than one Message subclass per item — eleven near-identical
359
+ classes would add nothing an `action` string doesn't already say,
360
+ and CCTApp.on_nav_action is the one place allowed to decide what
361
+ each id actually does."""
362
+
363
+ def __init__(self, action):
364
+ self.action = action
365
+ super().__init__()
366
+
367
+
368
+ class SettingChanged(Message):
369
+ """Merged Settings window (v0.7.6 Patch 1) -> app. `category` is
370
+ one of the setting ids the window renders ('perm_mode', 'theme',
371
+ 'ai_mode', 'model', 'workspace', 'autosave', 'sound', 'animation',
372
+ 'anim_speed', 'precision', 'clear_history'); `value` is the
373
+ proposed new value for choice-style categories (the mode/theme/AI
374
+ mode key, etc.) and None for pure actions and toggles. The window
375
+ never applies anything itself — CCTApp.on_setting_changed is the
376
+ only place that decides what a change means, then asks the window
377
+ to re-render its current values."""
378
+
379
+ def __init__(self, category, value=None):
380
+ self.category = category
381
+ self.value = value
382
+ super().__init__()
383
+
384
+
385
+ class WorkspaceFilesChanged(Message):
386
+ """Backend (agent tool loop) -> app: one or more files/folders on
387
+ disk were created/deleted/renamed by the AI during this turn, so the
388
+ Explorer tree (and, if the affected path is open, the Editor) need
389
+ to refresh. Deliberately coarse (a set of touched paths, not a
390
+ typed per-file event bus) — see CHANGELOG_v0.7.2 for why the full
391
+ "Workspace Event Bus" the spec describes is out of scope for this
392
+ slice."""
393
+
394
+ def __init__(self, paths=None):
395
+ self.paths = list(paths or [])
396
+ super().__init__()
397
+
398
+
399
+ class EditorTabsChanged(Message):
400
+ """EditorPane -> WorkspaceShell (and anything else listening): the
401
+ set of open file tabs changed (a file was opened or closed).
402
+ `count` is the number of open *files* (the placeholder Welcome tab
403
+ never counts). The shell uses this to decide whether the editor
404
+ pane should claim layout space at all and whether the narrow-
405
+ terminal Chat/Files switcher needs to exist — the shell never
406
+ reaches into EditorPane's private dicts for that, this event is the
407
+ only channel."""
408
+
409
+ def __init__(self, count):
410
+ self.count = count
411
+ super().__init__()
412
+
413
+
414
+ class ChatRequested(Message):
415
+ """WorkspaceShell's Chat/Files switcher -> app: the user asked to
416
+ see the AI chat (the one-click switch on narrow terminals, or the
417
+ editor's Esc "back to chat" step). The shell flips the panes
418
+ itself; the app only uses this to re-focus the composer so the user
419
+ can start typing right away — no other coordination crosses this
420
+ boundary."""
421
+
422
+ def __init__(self):
423
+ super().__init__()
424
+
425
+
426
+ # --------------------------------------------------------------------------
427
+ # v0.7.10 Live Web Preview — editor ⇄ browser pane events
428
+ #
429
+ class PreviewRequested(Message):
430
+ """EditorPane (▷ pill) / explorer -> app: preview this file as a
431
+ website. `path` is an absolute filesystem path to an HTML file (or
432
+ any file inside the site) — the controller detects the entry point
433
+ and web root from it."""
434
+
435
+ def __init__(self, path=None):
436
+ self.path = path
437
+ super().__init__()
438
+
439
+
440
+ class PreviewClosed(Message):
441
+ """PreviewPanel (⏻) / app -> shell: close Web Preview and return
442
+ the right pane to the Code Editor. The server/browser teardown and
443
+ any confirmation happen on the app side; the shell only flips its
444
+ WorkspaceMode when this arrives."""
445
+
446
+ def __init__(self):
447
+ super().__init__()
448
+
449
+
450
+ class BuildActivity(Message):
451
+ """Preview subsystem -> UI: one lightweight line of what CAT is
452
+ doing to the website ('● Writing style.css', '✓ Preview
453
+ synchronized'). Rendered in the PreviewPanel's activity strip —
454
+ NEVER dumped into the chat conversation (spec section 16)."""
455
+
456
+ def __init__(self, text, done=False):
457
+ self.text = text
458
+ self.done = done
459
+ super().__init__()
460
+
461
+
462
+ # v0.7.10 Live Diff Viewer events
463
+ class DiffViewerRequested(Message):
464
+ """EditorPane -> app: show the diff viewer for a file. `path` is
465
+ the absolute path to the file, `diff` is the computed diff lines."""
466
+
467
+ def __init__(self, path, diff=None):
468
+ self.path = path
469
+ self.diff = diff or []
470
+ super().__init__()
471
+
472
+
473
+ class DiffViewerClosed(Message):
474
+ """Diff viewer -> app: close the diff viewer panel."""
475
+
476
+ def __init__(self):
477
+ super().__init__()
478
+
479
+
480
+ class FileChangeTracked(Message):
481
+ """Editor -> app: a file was modified and the change was tracked.
482
+ Used for Live File Automation (#13) to notify other components."""
483
+
484
+ def __init__(self, path, change_type="modified"):
485
+ self.path = path
486
+ self.change_type = change_type # "created", "modified", "deleted"
487
+ super().__init__()