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,125 @@
1
+ """
2
+ CCT UI — command_palette.py (spec v0.7 project structure list + "Command
3
+ Highlighting" section: every command gets a category color, e.g. /solve
4
+ blue, /simulate purple, /gpu3d cyan, /build yellow, /plan green,
5
+ /websearch orange, /deepresearch magenta, /history gray, /help blue).
6
+
7
+ palette.py already implements the real engine this needs — fuzzy
8
+ subsequence matching (SuggestionEngine), live filtering as you type,
9
+ Up/Down navigation, Tab/Enter to accept (all driven from
10
+ composer.py's ComposerInput, unchanged here). Per the v0.7 brief ("Do
11
+ NOT rewrite working modules"), this file does not reimplement that;
12
+ it subclasses palette.CommandPalette and only changes how each row is
13
+ colored, then re-exports SuggestionEngine so callers can import either
14
+ name from this module going forward.
15
+ """
16
+
17
+ if __name__ == "__main__":
18
+ print("This is a library file and is not meant to be run directly.")
19
+ import sys
20
+ sys.exit(1)
21
+
22
+ from .palette import SuggestionEngine, TEXTUAL_AVAILABLE as _PALETTE_OK # noqa: F401
23
+
24
+ TEXTUAL_AVAILABLE = True
25
+ try:
26
+ from textual.containers import Vertical
27
+ from textual.widgets import Static
28
+ except Exception:
29
+ TEXTUAL_AVAILABLE = False
30
+
31
+ TEXTUAL_AVAILABLE = TEXTUAL_AVAILABLE and _PALETTE_OK
32
+
33
+ # Command (stripped of leading "/") -> category, used only to pick a
34
+ # color below. Anything not listed here falls back to "default".
35
+ # Categories map 1:1 to the spec's "Command Highlighting" table.
36
+ _CATEGORY_OF = {
37
+ "solve": "solve", "calculator": "solve", "kinetics": "solve",
38
+ "derive": "solve", "electrochemistry": "solve", "formulas": "solve",
39
+
40
+ "sim3d": "simulate", "atomsim": "simulate", "orbitals": "simulate",
41
+ "orbitalgrid": "simulate", "bonding": "simulate", "bloch": "simulate",
42
+ "react": "simulate", "graph": "simulate",
43
+
44
+ "gpu3d": "gpu3d",
45
+
46
+ "build": "build", "codepad": "build", "edit": "build",
47
+
48
+ "plan": "plan", "mode": "plan", "notebook": "plan", "agent": "plan", "ai": "plan",
49
+ "research": "research", "debug": "debug",
50
+
51
+ "install": "install", "packages": "install", "pipeline": "pipeline",
52
+ "orchestrate": "pipeline", "devices": "devices",
53
+
54
+ "websearch": "websearch",
55
+
56
+ "history": "history", "memory": "history", "workspace": "history",
57
+
58
+ "help": "help", "about": "help", "shortcuts": "help",
59
+ }
60
+
61
+ # Category -> semantic COLOR ROLE, resolved against the CURRENT theme at
62
+ # call time (theme.role_hex). v0.7.9.0 light-mode fix: these used to be
63
+ # hardcoded Tokyo-Night hexes whose pastels (#c0caf5 default!) were
64
+ # near-invisible on the light theme's white surfaces.
65
+ _CATEGORY_ROLE = {
66
+ "solve": "blue",
67
+ "simulate": "purple",
68
+ "gpu3d": "cyan",
69
+ "build": "amber",
70
+ "plan": "green",
71
+ "websearch": "amber",
72
+ "deepresearch": "purple",
73
+ "research": "cyan",
74
+ "debug": "red",
75
+ "install": "green",
76
+ "pipeline": "cyan",
77
+ "devices": "purple",
78
+ "history": "gray",
79
+ "help": "blue",
80
+ }
81
+
82
+
83
+ def category_color(cmd):
84
+ """`cmd` includes the leading '/'. Public so composer.py can color
85
+ a fully-typed command the same way the palette rows are colored.
86
+ Resolves through theme.role_hex so every theme stays readable —
87
+ v0.7.9.5: the old dependency-free Tokyo-Night fallback table is
88
+ gone (it leaked dark pastels onto light surfaces when the theme
89
+ module was briefly unimportable); the neutral gray fallback is
90
+ readable on BOTH dark and light backgrounds."""
91
+ role = _CATEGORY_ROLE.get(_CATEGORY_OF.get(cmd.lstrip("/"), ""), None)
92
+ try:
93
+ from .. import theme
94
+ if role:
95
+ return theme.role_hex(role)
96
+ r, g, b = theme.TEXT
97
+ return f"#{r:02x}{g:02x}{b:02x}"
98
+ except Exception:
99
+ return "#888888"
100
+
101
+
102
+ if TEXTUAL_AVAILABLE:
103
+ from .palette import CommandPalette as _BaseCommandPalette
104
+
105
+ class CommandPalette(_BaseCommandPalette):
106
+ """Same behavior as palette.CommandPalette (fuzzy match, live
107
+ filter, arrow nav) — only `_redraw` changes, to paint each
108
+ command in its category color instead of the flat accent color.
109
+ """
110
+
111
+ def _redraw(self):
112
+ from . import theme_css
113
+ accent = theme_css.current_hex("accent")
114
+ for child in list(self.children):
115
+ child.remove()
116
+ for i, (item, desc) in enumerate(self._matches):
117
+ classes = "cct-cmd-row" + (" cct-cmd-row-selected" if i == self._selected else "")
118
+ if item.startswith("/"):
119
+ color = category_color(item)
120
+ self.mount(Static(f"[{color} b]{item}[/] [dim]{desc}[/dim]", classes=classes))
121
+ else:
122
+ self.mount(Static(f"[{accent}]\u2726[/] [b]{item}[/] [dim]{desc}[/dim]", classes=classes))
123
+
124
+ else:
125
+ CommandPalette = None
@@ -0,0 +1,166 @@
1
+ """CAT Code Editor — IDE Command Palette Modal (calc_terminal/ui/command_palette_modal.py).
2
+
3
+ VS Code-style Command Palette modal triggered by Ctrl+Shift+P:
4
+ - Fuzzy searches all registered IDE commands from CommandRegistry.
5
+ - Displays category, title, description, and keybinding badges.
6
+ - Up/Down navigation, Enter to execute, Esc to dismiss.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import List, Optional, Tuple
12
+
13
+ TEXTUAL_AVAILABLE = True
14
+ try:
15
+ from rich.text import Text
16
+ from textual.app import ComposeResult
17
+ from textual.binding import Binding
18
+ from textual.containers import Vertical, VerticalScroll
19
+ from textual.screen import ModalScreen
20
+ from textual.widgets import Input, Static
21
+ except Exception:
22
+ TEXTUAL_AVAILABLE = False
23
+ ModalScreen = object # type: ignore
24
+
25
+ from ..editor.commands import CATCommand, get_command_registry
26
+ from ..editor.shortcuts import get_shortcut_manager
27
+
28
+
29
+ class CommandPaletteModal(ModalScreen):
30
+ """Full-featured IDE Command Palette modal dialog."""
31
+
32
+ BINDINGS = [
33
+ Binding("escape", "dismiss_modal", "Dismiss", show=False),
34
+ Binding("up", "move_up", "Move Up", show=False),
35
+ Binding("down", "move_down", "Move Down", show=False),
36
+ ]
37
+
38
+ DEFAULT_CSS = """
39
+ CommandPaletteModal {
40
+ align: center top;
41
+ background: rgba(0, 0, 0, 0.65);
42
+ padding-top: 3;
43
+ }
44
+ #cct-cmd-modal-box {
45
+ width: 76;
46
+ height: auto;
47
+ max-height: 24;
48
+ background: $surface;
49
+ border: round $accent;
50
+ padding: 0;
51
+ }
52
+ #cct-cmd-modal-input {
53
+ dock: top;
54
+ border: none;
55
+ border-bottom: solid $border;
56
+ background: $surface-alt;
57
+ padding: 0 1;
58
+ margin: 0;
59
+ }
60
+ #cct-cmd-modal-scroll {
61
+ height: auto;
62
+ max-height: 18;
63
+ padding: 0;
64
+ }
65
+ .cct-cmd-modal-row {
66
+ height: 1;
67
+ padding: 0 1;
68
+ background: transparent;
69
+ color: $text;
70
+ }
71
+ .cct-cmd-modal-row-selected {
72
+ background: $accent;
73
+ color: $app-background;
74
+ text-style: bold;
75
+ }
76
+ """
77
+
78
+ def __init__(self) -> None:
79
+ super().__init__()
80
+ self._matches: List[Tuple[CATCommand, str]] = []
81
+ self._selected_index: int = 0
82
+
83
+ def compose(self) -> ComposeResult:
84
+ with Vertical(id="cct-cmd-modal-box"):
85
+ yield Input(placeholder="Type a command or search actions...", id="cct-cmd-modal-input")
86
+ with VerticalScroll(id="cct-cmd-modal-scroll"):
87
+ yield Static("", id="cct-cmd-modal-list")
88
+
89
+ def on_mount(self) -> None:
90
+ self._filter_commands("")
91
+ try:
92
+ self.query_one("#cct-cmd-modal-input", Input).focus()
93
+ except Exception:
94
+ pass
95
+
96
+ def on_input_changed(self, event: Input.Changed) -> None:
97
+ if event.input.id == "cct-cmd-modal-input":
98
+ self._filter_commands(event.value.strip())
99
+
100
+ def on_input_submitted(self, event: Input.Submitted) -> None:
101
+ self._execute_selected()
102
+
103
+ def _filter_commands(self, query: str) -> None:
104
+ reg = get_command_registry()
105
+ shortcut_mgr = get_shortcut_manager()
106
+ all_cmds = reg.list_commands()
107
+ q = query.lower()
108
+
109
+ matches = []
110
+ for cmd in all_cmds:
111
+ keys = shortcut_mgr.get_keybindings_for_command(cmd.id)
112
+ key_str = f" [{keys[0].upper()}]" if keys else ""
113
+ if not q or q in cmd.title.lower() or q in cmd.id.lower() or q in cmd.category.lower():
114
+ matches.append((cmd, key_str))
115
+
116
+ self._matches = matches
117
+ self._selected_index = 0
118
+ self._render_rows()
119
+
120
+ def _render_rows(self) -> None:
121
+ try:
122
+ list_widget = self.query_one("#cct-cmd-modal-list", Static)
123
+ if not self._matches:
124
+ list_widget.update("[dim italic] No matching commands found.[/dim italic]")
125
+ return
126
+
127
+ lines = []
128
+ for i, (cmd, key_str) in enumerate(self._matches[:40]):
129
+ is_sel = (i == self._selected_index)
130
+ prefix = "▶ " if is_sel else " "
131
+ cat_tag = f"[{cmd.category}] " if cmd.category else ""
132
+ badge = f" [dim]{key_str}[/dim]" if key_str else ""
133
+
134
+ if is_sel:
135
+ lines.append(f"[reverse bold]{prefix}{cat_tag}{cmd.title}{badge}[/reverse bold]")
136
+ else:
137
+ lines.append(f"{prefix}[cyan]{cat_tag}[/cyan]{cmd.title}{badge}")
138
+
139
+ list_widget.update("\n".join(lines))
140
+ except Exception:
141
+ pass
142
+
143
+ def action_move_up(self) -> None:
144
+ if self._matches:
145
+ self._selected_index = max(0, self._selected_index - 1)
146
+ self._render_rows()
147
+
148
+ def action_move_down(self) -> None:
149
+ if self._matches:
150
+ self._selected_index = min(len(self._matches) - 1, self._selected_index + 1)
151
+ self._render_rows()
152
+
153
+ def action_dismiss_modal(self) -> None:
154
+ self.dismiss(None)
155
+
156
+ def _execute_selected(self) -> None:
157
+ if self._matches and 0 <= self._selected_index < len(self._matches):
158
+ cmd, _ = self._matches[self._selected_index]
159
+ self.dismiss(None)
160
+ try:
161
+ reg = get_command_registry()
162
+ app = getattr(self, "app", None)
163
+ ctx = {"app": app}
164
+ reg.execute(cmd.id, context=ctx)
165
+ except Exception:
166
+ pass