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,274 @@
1
+ """
2
+ Hidden easter eggs for Chemistry Calc Terminal.
3
+
4
+ None of these are listed in /help or /shortcuts on purpose — /shortcuts
5
+ just teases that they exist. They're triggered by typing an exact
6
+ phrase at the home prompt (checked in App.handle before the normal
7
+ free-text question pipeline), so they never interfere with real
8
+ chemistry questions.
9
+ """
10
+
11
+ import random
12
+ import sys
13
+ import time
14
+
15
+ from . import theme
16
+ from . import sound
17
+ from . import keys
18
+ from . import identity
19
+ from . import aicore
20
+ from .atomsim import ELEMENTS
21
+ from .engine import WIDTH
22
+
23
+
24
+ # ------------------------------------------------------------- helpers --
25
+ def _panel(lines, title, color=theme.PURPLE, grad=None):
26
+ print()
27
+ print(theme.panel(lines, title=title, color=color, width=WIDTH, title_gradient=grad))
28
+ print()
29
+
30
+
31
+ def _mark(app, key):
32
+ app.eggs_found.add(key)
33
+
34
+
35
+ # --------------------------------------------------------------- eggs --
36
+ def _egg_42(app):
37
+ _mark(app, "42")
38
+ sound.play("success")
39
+ _panel([
40
+ theme.gradient("THE ANSWER", theme.CYAN, theme.PURPLE, bold=True),
41
+ "",
42
+ theme.text("42 is the Answer to the Ultimate Question of Life,"),
43
+ theme.text("the Universe, and Everything."),
44
+ "",
45
+ theme.dim("It also happens to be the atomic number of ") + theme.orange("Molybdenum (Mo)", bold=True) + theme.dim("."),
46
+ theme.dim("Deep Thought would probably have appreciated a transition metal."),
47
+ ], "\u2728 easter egg", grad=(theme.CYAN, theme.PURPLE))
48
+
49
+
50
+ def _egg_gold(app):
51
+ _mark(app, "gold")
52
+ sound.play("success")
53
+ bar = theme.gradient("\u2588" * 30, (201, 176, 55), (255, 223, 128), (201, 176, 55), bold=True)
54
+ _panel([
55
+ theme.orange("Au \u00b7 79 \u00b7 196.97 u", bold=True),
56
+ bar,
57
+ "",
58
+ theme.dim("Gold is so unreactive it's found in nature as the pure metal \u2014"),
59
+ theme.dim("which is exactly why alchemists never managed to make it from lead."),
60
+ ], "\u2728 you struck gold", grad=((201, 176, 55), (255, 223, 128)))
61
+
62
+
63
+ def _egg_avogadro(app):
64
+ _mark(app, "avogadro")
65
+ sound.play("success")
66
+ _panel([
67
+ theme.cyan("6.022 \u00d7 10\u00b2\u00b3", bold=True) + theme.dim(" \u2014 one mole, give or take."),
68
+ "",
69
+ theme.dim("That's roughly how many grains of sand would cover the entire"),
70
+ theme.dim("Earth in a layer several metres deep. Chemistry deals in numbers"),
71
+ theme.dim("so big they stop being numbers and start being vibes."),
72
+ ], "\u2728 mole moment", grad=(theme.CYAN, theme.GREEN))
73
+
74
+
75
+ def _egg_coffee(app):
76
+ _mark(app, "coffee")
77
+ sound.play("notify")
78
+ cup = [
79
+ " ( (",
80
+ " ) )",
81
+ " .______.",
82
+ " | |]",
83
+ " \\ /",
84
+ " `----'",
85
+ ]
86
+ lines = [theme.dim(l) for l in cup]
87
+ lines.append("")
88
+ lines.append(theme.text("Caffeine", bold=True) + theme.dim(" \u2014 C\u2088H\u2081\u2080N\u2084O\u2082"))
89
+ lines.append(theme.dim("A methylxanthine alkaloid, and the reason this terminal exists."))
90
+ _panel(lines, "\u2728 brewed", grad=(theme.ORANGE, theme.PURPLE))
91
+
92
+
93
+ def _egg_sandwich(app):
94
+ _mark(app, "sandwich")
95
+ sound.play("success")
96
+ _panel([
97
+ theme.green("Sandwich synthesized.", bold=True),
98
+ theme.dim("Yield: 100% \u00b7 Byproducts: crumbs \u00b7 Reaction time: instantaneous"),
99
+ "",
100
+ theme.dim("(root privileges confirmed \u2014 you asked nicely, so it worked this time)"),
101
+ ], "\u2728 sudo", grad=(theme.GREEN, theme.CYAN))
102
+
103
+
104
+ def _egg_who(app):
105
+ _mark(app, "who")
106
+ sound.play("notify")
107
+ banner = theme.gradient("CHEMISTRY CALC TERMINAL", theme.CYAN, theme.PURPLE, theme.CYAN, bold=True)
108
+ try:
109
+ config = aicore.load_config()
110
+ except Exception:
111
+ config = None
112
+ _panel([
113
+ banner,
114
+ "",
115
+ theme.text(f"Created by {identity.CREATOR_NAME}, {identity.CREATOR_ROLE}."),
116
+ theme.dim(f"{identity.DEVELOPMENT_NOTE.capitalize()}. Built to look like a dev CLI,"),
117
+ theme.dim("but every calculation underneath is real \u2014 the fractions, the"),
118
+ theme.dim("derivations, the atoms, all of it."),
119
+ theme.faint(f"Stack: {identity.tech_stack_sentence(config)}."),
120
+ "",
121
+ theme.faint(f"You've now found {len(app.eggs_found) + 1} hidden thing(s). Keep looking."),
122
+ ], "\u2728 credits", grad=(theme.CYAN, theme.PURPLE))
123
+
124
+
125
+ def _egg_model(app):
126
+ _mark(app, "model")
127
+ sound.play("notify")
128
+ try:
129
+ config = aicore.load_config()
130
+ except Exception:
131
+ config = None
132
+ _panel([
133
+ theme.gradient("ACTIVE MODEL", theme.CYAN, theme.PURPLE, bold=True),
134
+ "",
135
+ theme.text(identity.answer_model(config)),
136
+ ], "\u2728 model", grad=(theme.CYAN, theme.PURPLE))
137
+
138
+
139
+ def _egg_god(app):
140
+ _mark(app, "god")
141
+ sound.play("sim_start")
142
+ _panel([
143
+ theme.gradient("\u2588\u2588 GOD MODE \u2588\u2588", theme.RED, theme.ORANGE, bold=True),
144
+ "",
145
+ theme.dim("Unlimited moles granted. Entropy locally reversed. Please use"),
146
+ theme.dim("responsibly \u2014 the second law of thermodynamics is watching."),
147
+ "",
148
+ theme.faint("(no actual cheats here \u2014 just a nod to every '90s game you know)"),
149
+ ], "\u2728 iddqd", grad=(theme.RED, theme.ORANGE))
150
+
151
+
152
+ def _matrix_rain(app):
153
+ """A Matrix-style falling-code effect, except the "code" is real
154
+ element symbols — press q to stop early."""
155
+ _mark(app, "matrix")
156
+ sound.play("sim_start")
157
+ width, height = WIDTH, 20
158
+ symbols = [v[0] for v in ELEMENTS.values()]
159
+ cols = [random.randint(-height, 0) for _ in range(width // 2)]
160
+ speeds = [random.choice([1, 1, 2]) for _ in cols]
161
+ theme.hide_cursor()
162
+ try:
163
+ for frame in range(90):
164
+ grid = [[" "] * width for _ in range(height)]
165
+ for ci, c in enumerate(cols):
166
+ x = ci * 2
167
+ head = c
168
+ for trail in range(10):
169
+ y = head - trail
170
+ if 0 <= y < height and 0 <= x < width:
171
+ sym = random.choice(symbols)
172
+ if trail == 0:
173
+ grid[y][x] = theme.fg(sym[0], (210, 255, 210), bold=True)
174
+ else:
175
+ fade = max(0, 1 - trail / 10)
176
+ col = theme.lerp((10, 40, 15), (80, 220, 110), fade)
177
+ grid[y][x] = theme.fg(sym[0], col)
178
+ cols[ci] += speeds[ci % len(speeds)]
179
+ if cols[ci] - 10 > height:
180
+ cols[ci] = random.randint(-height, -1)
181
+ theme.cursor_home()
182
+ out = "\n".join("".join(row) for row in grid)
183
+ sys.stdout.write(out + "\n")
184
+ sys.stdout.write(theme.faint(" press q to stop\u2026") + "\n")
185
+ sys.stdout.flush()
186
+ if keys.key_available():
187
+ k = keys.read_key()
188
+ if k and k.lower() == "q":
189
+ break
190
+ time.sleep(0.05)
191
+ finally:
192
+ theme.show_cursor()
193
+ print()
194
+ print(theme.gradient(" There is no calculator. Only chemistry.", theme.GREEN, theme.CYAN, bold=True))
195
+ print()
196
+
197
+
198
+ def _egg_3d(app):
199
+ _mark(app, "3d")
200
+ sound.play("notify")
201
+ art = [
202
+ " +---------+",
203
+ " /| /|",
204
+ " / | / |",
205
+ " +--+------+ /",
206
+ " | | | +",
207
+ " | +------+-|",
208
+ " | / |/",
209
+ " +---------+",
210
+ ]
211
+ lines = [theme.gradient(l, theme.CYAN, theme.PURPLE) for l in art]
212
+ lines.insert(0, "")
213
+ lines.append("")
214
+ lines.append(theme.dim(" A glimpse into the third dimension..."))
215
+ _panel(lines, "\u2728 3d art", grad=(theme.CYAN, theme.PURPLE))
216
+
217
+
218
+ EGGS = {
219
+ "42": _egg_42,
220
+ "gold": _egg_gold,
221
+ "au": _egg_gold,
222
+ "avogadro": _egg_avogadro,
223
+ "avogadro's number": _egg_avogadro,
224
+ "avogadro number": _egg_avogadro,
225
+ "coffee": _egg_coffee,
226
+ "caffeine": _egg_coffee,
227
+ "sudo make me a sandwich": _egg_sandwich,
228
+ "make me a sandwich": _egg_sandwich,
229
+ "who made this": _egg_who,
230
+ "who made you": _egg_who,
231
+ "who are you": _egg_who,
232
+ "what are you": _egg_who,
233
+ "who built this": _egg_who,
234
+ "who built cct": _egg_who,
235
+ "who built you": _egg_who,
236
+ "who created this": _egg_who,
237
+ "who created you": _egg_who,
238
+ "who created cct": _egg_who,
239
+ "who developed this": _egg_who,
240
+ "who developed you": _egg_who,
241
+ "who developed cct": _egg_who,
242
+ "who owns this project": _egg_who,
243
+ "who owns cct": _egg_who,
244
+ "what is cct": _egg_who,
245
+ "iddqd": _egg_god,
246
+ "konami": _egg_god,
247
+ "/matrix": _matrix_rain,
248
+ "matrix": _matrix_rain,
249
+ "3d": _egg_3d,
250
+ "show me something 3d": _egg_3d,
251
+ "cube": _egg_3d,
252
+ }
253
+
254
+
255
+ def check(low, app):
256
+ """Return True if `low` (already lowercased/stripped) matched a
257
+ hidden easter egg and was handled."""
258
+ fn = EGGS.get(low)
259
+ if fn is not None:
260
+ fn(app)
261
+ return True
262
+ # Exact-match dict above misses natural phrasing variants ("who is
263
+ # the developer of cct?", "who owns this project?" with punctuation,
264
+ # etc.) — fall back to identity.py's looser matcher so those still
265
+ # land on the same, correct answer instead of falling through to the
266
+ # AI (or a "couldn't match that" error).
267
+ kind = identity.classify_identity_question(low)
268
+ if kind == "creator" or kind == "what":
269
+ _egg_who(app)
270
+ return True
271
+ if kind == "model":
272
+ _egg_model(app)
273
+ return True
274
+ return False
@@ -0,0 +1 @@
1
+ """CAT Code Editor - Subsystem modules (commands, shortcuts, actions)."""
@@ -0,0 +1,263 @@
1
+ """CAT Code Editor — Editor Actions (calc_terminal/editor/actions.py).
2
+
3
+ Concrete editor manipulation routines:
4
+ - Toggle line comment
5
+ - Delete line
6
+ - Move line up / down
7
+ - Copy line up / down
8
+ - Insert line below / above
9
+ - Select next matching occurrence
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any, Optional
15
+
16
+ _COMMENT_PREFIXES = {
17
+ "python": "# ",
18
+ "bash": "# ",
19
+ "sh": "# ",
20
+ "yaml": "# ",
21
+ "yml": "# ",
22
+ "toml": "# ",
23
+ "javascript": "// ",
24
+ "typescript": "// ",
25
+ "json": "// ",
26
+ "rust": "// ",
27
+ "go": "// ",
28
+ "java": "// ",
29
+ "c": "// ",
30
+ "cpp": "// ",
31
+ "css": "/* ",
32
+ "sql": "-- ",
33
+ "html": "<!-- ",
34
+ }
35
+
36
+
37
+ def toggle_line_comment(area: Any, language: Optional[str] = None) -> bool:
38
+ """Toggle comment on the current line or selection in a TextArea."""
39
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
40
+ return False
41
+ lang = (language or getattr(area, "language", "") or "").lower()
42
+ prefix = _COMMENT_PREFIXES.get(lang, "# ")
43
+ suffix = " -->" if prefix == "<!-- " else (" */" if prefix == "/* " else "")
44
+
45
+ lines = area.text.splitlines() or [""]
46
+ row, col = area.cursor_location
47
+ if row < 0 or row >= len(lines):
48
+ return False
49
+
50
+ line = lines[row]
51
+ stripped = line.lstrip()
52
+ indent = line[:len(line) - len(stripped)]
53
+
54
+ if suffix:
55
+ if stripped.startswith(prefix.strip()) and stripped.endswith(suffix.strip()):
56
+ uncommented = stripped[len(prefix.strip()):-len(suffix.strip())].strip()
57
+ lines[row] = indent + uncommented
58
+ else:
59
+ lines[row] = indent + prefix + stripped + suffix
60
+ else:
61
+ if stripped.startswith(prefix):
62
+ lines[row] = indent + stripped[len(prefix):]
63
+ elif stripped.startswith(prefix.strip()):
64
+ lines[row] = indent + stripped[len(prefix.strip()):]
65
+ else:
66
+ lines[row] = indent + prefix + stripped
67
+
68
+ new_text = "\n".join(lines)
69
+ area.text = new_text
70
+ try:
71
+ area.move_cursor((row, min(col, len(lines[row]))))
72
+ except Exception:
73
+ pass
74
+ return True
75
+
76
+
77
+ def delete_line(area: Any) -> bool:
78
+ """Delete the active line in a TextArea."""
79
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
80
+ return False
81
+ lines = area.text.splitlines() or [""]
82
+ row, col = area.cursor_location
83
+ if row < 0 or row >= len(lines):
84
+ return False
85
+
86
+ if len(lines) == 1:
87
+ lines = [""]
88
+ target_row = 0
89
+ else:
90
+ lines.pop(row)
91
+ target_row = min(row, len(lines) - 1)
92
+
93
+ area.text = "\n".join(lines)
94
+ try:
95
+ area.move_cursor((target_row, min(col, len(lines[target_row]))))
96
+ except Exception:
97
+ pass
98
+ return True
99
+
100
+
101
+ def move_line_up(area: Any) -> bool:
102
+ """Swap the active line with the line above it."""
103
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
104
+ return False
105
+ lines = area.text.splitlines() or [""]
106
+ row, col = area.cursor_location
107
+ if row <= 0 or row >= len(lines):
108
+ return False
109
+
110
+ lines[row - 1], lines[row] = lines[row], lines[row - 1]
111
+ area.text = "\n".join(lines)
112
+ try:
113
+ area.move_cursor((row - 1, min(col, len(lines[row - 1]))))
114
+ except Exception:
115
+ pass
116
+ return True
117
+
118
+
119
+ def move_line_down(area: Any) -> bool:
120
+ """Swap the active line with the line below it."""
121
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
122
+ return False
123
+ lines = area.text.splitlines() or [""]
124
+ row, col = area.cursor_location
125
+ if row < 0 or row >= len(lines) - 1:
126
+ return False
127
+
128
+ lines[row + 1], lines[row] = lines[row], lines[row + 1]
129
+ area.text = "\n".join(lines)
130
+ try:
131
+ area.move_cursor((row + 1, min(col, len(lines[row + 1]))))
132
+ except Exception:
133
+ pass
134
+ return True
135
+
136
+
137
+ def copy_line_up(area: Any) -> bool:
138
+ """Duplicate current line upwards."""
139
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
140
+ return False
141
+ lines = area.text.splitlines() or [""]
142
+ row, col = area.cursor_location
143
+ if row < 0 or row >= len(lines):
144
+ return False
145
+
146
+ lines.insert(row, lines[row])
147
+ area.text = "\n".join(lines)
148
+ try:
149
+ area.move_cursor((row, col))
150
+ except Exception:
151
+ pass
152
+ return True
153
+
154
+
155
+ def copy_line_down(area: Any) -> bool:
156
+ """Duplicate current line downwards."""
157
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
158
+ return False
159
+ lines = area.text.splitlines() or [""]
160
+ row, col = area.cursor_location
161
+ if row < 0 or row >= len(lines):
162
+ return False
163
+
164
+ lines.insert(row + 1, lines[row])
165
+ area.text = "\n".join(lines)
166
+ try:
167
+ area.move_cursor((row + 1, col))
168
+ except Exception:
169
+ pass
170
+ return True
171
+
172
+
173
+ def insert_line_below(area: Any) -> bool:
174
+ """Insert a new line below and move cursor to it."""
175
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
176
+ return False
177
+ lines = area.text.splitlines() or [""]
178
+ row, col = area.cursor_location
179
+ if row < 0 or row >= len(lines):
180
+ row = len(lines) - 1
181
+
182
+ current_line = lines[row] if row < len(lines) else ""
183
+ indent = current_line[:len(current_line) - len(current_line.lstrip())]
184
+
185
+ lines.insert(row + 1, indent)
186
+ area.text = "\n".join(lines)
187
+ try:
188
+ area.move_cursor((row + 1, len(indent)))
189
+ except Exception:
190
+ pass
191
+ return True
192
+
193
+
194
+ def insert_line_above(area: Any) -> bool:
195
+ """Insert a new line above and move cursor to it."""
196
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
197
+ return False
198
+ lines = area.text.splitlines() or [""]
199
+ row, col = area.cursor_location
200
+ if row < 0:
201
+ row = 0
202
+
203
+ current_line = lines[row] if row < len(lines) else ""
204
+ indent = current_line[:len(current_line) - len(current_line.lstrip())]
205
+
206
+ lines.insert(row, indent)
207
+ area.text = "\n".join(lines)
208
+ try:
209
+ area.move_cursor((row, len(indent)))
210
+ except Exception:
211
+ pass
212
+ return True
213
+
214
+
215
+ def select_next_occurrence(area: Any) -> bool:
216
+ """Select next matching occurrence of current selection or word under cursor."""
217
+ if not hasattr(area, "text") or not hasattr(area, "cursor_location"):
218
+ return False
219
+ sel = getattr(area, "selected_text", None)
220
+ if not sel:
221
+ # Get word under cursor
222
+ lines = area.text.splitlines() or [""]
223
+ row, col = area.cursor_location
224
+ if row < len(lines):
225
+ line = lines[row]
226
+ start = col
227
+ while start > 0 and (line[start - 1].isalnum() or line[start - 1] == "_"):
228
+ start -= 1
229
+ end = col
230
+ while end < len(line) and (line[end].isalnum() or line[end] == "_"):
231
+ end += 1
232
+ sel = line[start:end]
233
+ if sel and hasattr(area, "selection"):
234
+ try:
235
+ area.selection = area.selection.__class__((row, start), (row, end))
236
+ except Exception:
237
+ pass
238
+
239
+ if not sel:
240
+ return False
241
+
242
+ # Find next occurrence after cursor
243
+ full_text = area.text
244
+ lines = full_text.splitlines() or [""]
245
+ cur_row, cur_col = area.cursor_location
246
+ n = len(lines)
247
+ for i in range(n + 1):
248
+ row = (cur_row + i) % n if n else 0
249
+ line = lines[row] if row < len(lines) else ""
250
+ search_from = cur_col + len(sel) if i == 0 else 0
251
+ col = line.find(sel, search_from)
252
+ if col == -1 and i == 0:
253
+ col = line.find(sel)
254
+ if col != -1:
255
+ try:
256
+ area.move_cursor((row, col))
257
+ if hasattr(area, "selection"):
258
+ area.selection = area.selection.__class__((row, col), (row, col + len(sel)))
259
+ area.scroll_cursor_visible()
260
+ return True
261
+ except Exception:
262
+ pass
263
+ return False