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,368 @@
1
+ """
2
+ Live atom & electron simulation, rendered as real-time ASCII/ANSI
3
+ animation in the terminal (redrawn in-place, no external GUI needed).
4
+
5
+ Two simulators:
6
+
7
+ atom_simulation(z) -- Bohr planetary-model animation: a nucleus of
8
+ Z protons + N neutrons, with electrons
9
+ orbiting real K/L/M/N/... shells at
10
+ shell-dependent angular speed.
11
+
12
+ orbital_simulation(key) -- a Monte-Carlo "electron cloud" builder for
13
+ real hydrogen-like orbitals (1s..3d), using
14
+ the actual radial/angular probability
15
+ density |psi|^2, accumulated dot-by-dot
16
+ live, the way textbooks describe orbitals
17
+ being "probability clouds" rather than
18
+ fixed paths.
19
+ """
20
+
21
+ import math
22
+ import random
23
+ import sys
24
+ import time
25
+
26
+ from . import theme
27
+ from . import keys
28
+ from . import scires
29
+
30
+ # ------------------------------------------------------------- elements --
31
+ # Simplified Bohr-Bury shell model (K, L, M, N...) — the standard
32
+ # teaching model. Transition-metal shells (Z=21-30) are approximate.
33
+ ELEMENTS = {
34
+ 1: ("H", "Hydrogen", [1], 1), 2: ("He", "Helium", [2], 4),
35
+ 3: ("Li", "Lithium", [2, 1], 7), 4: ("Be", "Beryllium", [2, 2], 9),
36
+ 5: ("B", "Boron", [2, 3], 11), 6: ("C", "Carbon", [2, 4], 12),
37
+ 7: ("N", "Nitrogen", [2, 5], 14), 8: ("O", "Oxygen", [2, 6], 16),
38
+ 9: ("F", "Fluorine", [2, 7], 19), 10: ("Ne", "Neon", [2, 8], 20),
39
+ 11: ("Na", "Sodium", [2, 8, 1], 23), 12: ("Mg", "Magnesium", [2, 8, 2], 24),
40
+ 13: ("Al", "Aluminium", [2, 8, 3], 27), 14: ("Si", "Silicon", [2, 8, 4], 28),
41
+ 15: ("P", "Phosphorus", [2, 8, 5], 31), 16: ("S", "Sulfur", [2, 8, 6], 32),
42
+ 17: ("Cl", "Chlorine", [2, 8, 7], 35), 18: ("Ar", "Argon", [2, 8, 8], 40),
43
+ 19: ("K", "Potassium", [2, 8, 8, 1], 39), 20: ("Ca", "Calcium", [2, 8, 8, 2], 40),
44
+ 21: ("Sc", "Scandium", [2, 8, 9, 2], 45), 22: ("Ti", "Titanium", [2, 8, 10, 2], 48),
45
+ 23: ("V", "Vanadium", [2, 8, 11, 2], 51), 24: ("Cr", "Chromium", [2, 8, 13, 1], 52),
46
+ 25: ("Mn", "Manganese", [2, 8, 13, 2], 55), 26: ("Fe", "Iron", [2, 8, 14, 2], 56),
47
+ 27: ("Co", "Cobalt", [2, 8, 15, 2], 59), 28: ("Ni", "Nickel", [2, 8, 16, 2], 59),
48
+ 29: ("Cu", "Copper", [2, 8, 18, 1], 64), 30: ("Zn", "Zinc", [2, 8, 18, 2], 65),
49
+ 31: ("Ga", "Gallium", [2, 8, 18, 3], 70), 32: ("Ge", "Germanium", [2, 8, 18, 4], 73),
50
+ 33: ("As", "Arsenic", [2, 8, 18, 5], 75), 34: ("Se", "Selenium", [2, 8, 18, 6], 79),
51
+ 35: ("Br", "Bromine", [2, 8, 18, 7], 80), 36: ("Kr", "Krypton", [2, 8, 18, 8], 84),
52
+ }
53
+ SYMBOL_TO_Z = {v[0].lower(): k for k, v in ELEMENTS.items()}
54
+ SHELL_NAMES = ["K", "L", "M", "N", "O", "P"]
55
+
56
+
57
+ def resolve_element(query):
58
+ q = str(query).strip().lower()
59
+ if q.isdigit() and int(q) in ELEMENTS:
60
+ return int(q)
61
+ return SYMBOL_TO_Z.get(q)
62
+
63
+
64
+ # --------------------------------------------------------- bohr display --
65
+ def _plot(grid, x, y, ch, color_fn):
66
+ w, h = len(grid[0]), len(grid)
67
+ xi, yi = int(round(x)), int(round(y))
68
+ if 0 <= yi < h and 0 <= xi < w:
69
+ grid[yi][xi] = color_fn(ch)
70
+
71
+
72
+ def _render_bohr_frame(z, angle, width=64, height=22):
73
+ sym, name, shells, mass = ELEMENTS[z]
74
+ neutrons = mass - z
75
+ cx, cy = width // 2, height // 2
76
+ grid = [[" "] * width for _ in range(height)]
77
+
78
+ max_shell_r = min(cx - 3, cy - 2)
79
+ n_shells = len(shells)
80
+ radii = [max_shell_r * (i + 1) / n_shells for i in range(n_shells)]
81
+
82
+ # orbit rings (faint dotted circles), x-scaled 2:1 for terminal char aspect
83
+ for si, radius in enumerate(radii):
84
+ steps = int(radius * 10) + 12
85
+ for k in range(steps):
86
+ th = 2 * math.pi * k / steps
87
+ x = cx + radius * 2 * math.cos(th)
88
+ y = cy + radius * math.sin(th)
89
+ _plot(grid, x, y, "\u00b7", lambda c: theme.faint(c))
90
+
91
+ # nucleus: protons (red) + neutrons (gray) packed in a tiny cluster
92
+ random.seed(z * 7)
93
+ cluster = []
94
+ total_nucleons = z + neutrons
95
+ pack_r = max(1.4, math.sqrt(total_nucleons) * 0.55)
96
+ for i in range(min(total_nucleons, 40)):
97
+ ang = random.uniform(0, 2 * math.pi)
98
+ rr = random.uniform(0, pack_r)
99
+ cluster.append((cx + rr * 2 * math.cos(ang) * 0.6, cy + rr * math.sin(ang) * 0.6, i < z))
100
+ for x, y, is_proton in cluster:
101
+ ch = "P" if is_proton else "n"
102
+ color = (lambda c: theme.red(c, bold=True)) if is_proton else (lambda c: theme.dim(c))
103
+ _plot(grid, x, y, ch, color)
104
+
105
+ # electrons orbiting each shell — inner shells move faster (higher w)
106
+ electron_positions = []
107
+ for si, (count, radius) in enumerate(zip(shells, radii)):
108
+ speed = 1.0 / (si + 1) ** 0.7
109
+ for e in range(count):
110
+ phase = 2 * math.pi * e / max(count, 1)
111
+ th = angle * speed + phase
112
+ x = cx + radius * 2 * math.cos(th)
113
+ y = cy + radius * math.sin(th)
114
+ electron_positions.append((x, y, si))
115
+ _plot(grid, x, y, "\u25cf", lambda c: theme.cyan(c, bold=True))
116
+
117
+ lines = ["".join(row) for row in grid]
118
+
119
+ info = [
120
+ theme.purple(f"{name} ({sym})", bold=True) + theme.dim(f" Z={z} mass number \u2248 {mass}"),
121
+ theme.dim("Protons: ") + theme.red(str(z), bold=True) + " " +
122
+ theme.dim("Neutrons: ") + theme.fg(str(neutrons), theme.FAINT, bold=True) + " " +
123
+ theme.dim("Electrons: ") + theme.cyan(str(z), bold=True),
124
+ theme.dim("Shells (Bohr-Bury): ") + " ".join(
125
+ f"{SHELL_NAMES[i]}={c}" for i, c in enumerate(shells)),
126
+ ]
127
+ return lines, info
128
+
129
+
130
+ def _snapshot_bohr(z, angle):
131
+ """Save a scientific-style matplotlib PNG snapshot (glow-density
132
+ nucleus + electron shells on black) via calc_terminal.scires.
133
+ Returns the saved path, or None if matplotlib/numpy/scipy are
134
+ unavailable. `angle` is accepted for backward compatibility with
135
+ existing call sites but no longer affects the static snapshot."""
136
+ if z not in ELEMENTS:
137
+ return None
138
+ sym, name, shells, mass = ELEMENTS[z]
139
+ return scires.render_atom(z, sym, name, shells, mass,
140
+ out_name=f"atom_{sym.lower()}_snapshot.png")
141
+
142
+
143
+ def atom_simulation(z, out=sys.stdout, max_frames=600, frame_delay=0.06):
144
+ """Runs the live Bohr-model animation. Returns cleanly on 'q', on
145
+ frame limit, or on KeyboardInterrupt -- never crashes the app."""
146
+ if z not in ELEMENTS:
147
+ print(theme.red(f" No shell data for element Z={z} (supported: 1-36)."))
148
+ return
149
+
150
+ angle = 0.0
151
+ speed = 0.35
152
+ paused = False
153
+ status_msg = ""
154
+ interactive = keys.stdin_is_interactive()
155
+ theme.hide_cursor()
156
+ try:
157
+ theme.clear_screen()
158
+ frame = 0
159
+ while frame < max_frames:
160
+ lines, info = _render_bohr_frame(z, angle)
161
+ theme.cursor_home()
162
+ out.write(theme.panel(info + [""] + lines, title="atom · live simulation",
163
+ color=theme.CYAN, width=70))
164
+ out.write("\n" + theme.faint(
165
+ " [q] quit [space] pause [+/-] speed [n/p] next/prev [s] snapshot"
166
+ f" speed={speed:.2f}{' (paused)' if paused else ''}") + "\n")
167
+ if status_msg:
168
+ out.write(" " + theme.green(status_msg) + "\n")
169
+ status_msg = ""
170
+ if not interactive:
171
+ out.write(theme.dim(
172
+ " (no interactive terminal detected — showing a single "
173
+ "frame instead of the live view)\n"))
174
+ out.flush()
175
+
176
+ if not paused:
177
+ angle += speed
178
+ frame += 1
179
+
180
+ if not interactive:
181
+ break
182
+
183
+ k = keys.read_key() if keys.key_available() else ""
184
+ if k == "q":
185
+ break
186
+ elif k == " ":
187
+ paused = not paused
188
+ elif k == "+":
189
+ speed = min(3.0, speed + 0.1)
190
+ elif k == "-":
191
+ speed = max(0.05, speed - 0.1)
192
+ elif k == "n":
193
+ nxt = min(36, z + 1)
194
+ if nxt in ELEMENTS:
195
+ z = nxt
196
+ elif k == "p":
197
+ prv = max(1, z - 1)
198
+ if prv in ELEMENTS:
199
+ z = prv
200
+ elif k == "r":
201
+ angle = 0.0
202
+ elif k == "s":
203
+ path = _snapshot_bohr(z, angle)
204
+ status_msg = f"Saved: {path}" if path else "matplotlib not installed — snapshot skipped."
205
+
206
+ time.sleep(frame_delay)
207
+ except KeyboardInterrupt:
208
+ pass
209
+ finally:
210
+ theme.show_cursor()
211
+ print()
212
+
213
+
214
+ # ------------------------------------------------------ quantum orbitals --
215
+ # Real (unnormalized, relative) hydrogen-like radial & angular probability
216
+ # density functions. a0 units. Good enough for a live, correctly-shaped
217
+ # probability-cloud visualization keyed by real quantum numbers n, l, m.
218
+ ORBITALS = {
219
+ "1s": (1, 0, 0), "2s": (2, 0, 0), "2p": (2, 1, 0),
220
+ "3s": (3, 0, 0), "3p": (3, 1, 0), "3d": (3, 2, 0),
221
+ }
222
+
223
+
224
+ def _radial_density(n, l, r):
225
+ # r in units of a0. Standard hydrogen radial shapes (unnormalized).
226
+ if (n, l) == (1, 0):
227
+ R = math.exp(-r)
228
+ elif (n, l) == (2, 0):
229
+ R = (2 - r) * math.exp(-r / 2)
230
+ elif (n, l) == (2, 1):
231
+ R = r * math.exp(-r / 2)
232
+ elif (n, l) == (3, 0):
233
+ R = (27 - 18 * r + 2 * r * r) * math.exp(-r / 3)
234
+ elif (n, l) == (3, 1):
235
+ R = r * (6 - r) * math.exp(-r / 3)
236
+ elif (n, l) == (3, 2):
237
+ R = r * r * math.exp(-r / 3)
238
+ else:
239
+ R = math.exp(-r)
240
+ return (R * R) * (r * r) # include r^2 volume-element weighting
241
+
242
+
243
+ def _angular_density(l, m, theta):
244
+ if l == 0:
245
+ return 1.0
246
+ if l == 1:
247
+ return math.cos(theta) ** 2 # 2pz-type lobe, x-z cross-section
248
+ if l == 2:
249
+ return (3 * math.cos(theta) ** 2 - 1) ** 2 # dz2-type
250
+ return 1.0
251
+
252
+
253
+ def _sample_point(n, l, m, r_max):
254
+ """Rejection-sample an (r, theta) pair from the real |psi|^2 density."""
255
+ peak = 0.0
256
+ for rr in [i * r_max / 60 for i in range(61)]:
257
+ for th in [j * math.pi / 20 for j in range(21)]:
258
+ v = _radial_density(n, l, rr) * _angular_density(l, m, th)
259
+ peak = max(peak, v)
260
+ peak = max(peak, 1e-9)
261
+ for _ in range(200):
262
+ r = random.uniform(0, r_max)
263
+ th = random.uniform(0, math.pi)
264
+ val = _radial_density(n, l, r) * _angular_density(l, m, th)
265
+ if random.uniform(0, peak) <= val:
266
+ phi = random.uniform(0, 2 * math.pi)
267
+ return r, th, phi
268
+ return None
269
+
270
+
271
+ def _snapshot_orbital(orbital_key, n, l, m, r_max):
272
+ """Save a scientific-style probability-density point-cloud PNG
273
+ (density-colored magma scatter on black) via calc_terminal.scires.
274
+ `r_max` is accepted for backward compatibility with existing call
275
+ sites; scires picks its own r_max per n internally."""
276
+ return scires.render_orbital(orbital_key, n, l, m,
277
+ out_name=f"orbital_{orbital_key}_snapshot.png")
278
+
279
+
280
+ def snapshot_orbital_grid():
281
+ """Save a reference-chart-style grid of many hydrogen orbitals'
282
+ |psi|^2 (like the classic 'Hydrogen Wave Function' chart). Returns
283
+ the saved path, or None if matplotlib/numpy/scipy are unavailable."""
284
+ return scires.render_orbital_grid()
285
+
286
+
287
+ def orbital_simulation(orbital_key, out=sys.stdout, max_points=2200, width=62, height=26):
288
+ """Live Monte-Carlo build-up of a real quantum orbital's probability
289
+ cloud (the textbook 'electron cloud'), redrawn as it accumulates."""
290
+ if orbital_key not in ORBITALS:
291
+ print(theme.red(f" Unknown orbital '{orbital_key}'. Try 1s, 2s, 2p, 3s, 3p, 3d."))
292
+ return
293
+ n, l, m = ORBITALS[orbital_key]
294
+ r_max = {1: 6, 2: 14, 3: 24}[n]
295
+
296
+ grid = [[0 for _ in range(width)] for _ in range(height)]
297
+ cx, cy = width // 2, height // 2
298
+ shade = " .:-=+*#%@"
299
+
300
+ theme.hide_cursor()
301
+ placed = 0
302
+ paused = False
303
+ status_msg = ""
304
+ try:
305
+ theme.clear_screen()
306
+ while placed < max_points:
307
+ k = keys.read_key() if keys.key_available() else ""
308
+ if k == "q":
309
+ break
310
+ if k == " ":
311
+ paused = not paused
312
+ if k == "r":
313
+ grid = [[0 for _ in range(width)] for _ in range(height)]
314
+ placed = 0
315
+ if k == "s":
316
+ path = _snapshot_orbital(orbital_key, n, l, m, r_max)
317
+ status_msg = f"Saved: {path}" if path else "matplotlib not installed — snapshot skipped."
318
+
319
+ if not paused:
320
+ for _ in range(6):
321
+ pt = _sample_point(n, l, m, r_max)
322
+ if pt is None:
323
+ continue
324
+ r, th, phi = pt
325
+ # project onto the x-z plane (side-on cross-section)
326
+ x = r * math.sin(th) * math.cos(phi)
327
+ z_ = r * math.cos(th)
328
+ scale = (width / 2 - 2) / r_max
329
+ gx = int(round(cx + x * scale * 2))
330
+ gy = int(round(cy - z_ * scale))
331
+ if 0 <= gx < width and 0 <= gy < height:
332
+ grid[gy][gx] = min(len(shade) - 1, grid[gy][gx] + 1)
333
+ placed += 1
334
+
335
+ rows = []
336
+ for row in grid:
337
+ line = ""
338
+ for v in row:
339
+ ch = shade[min(v, len(shade) - 1)]
340
+ if v == 0:
341
+ line += " "
342
+ elif v < 3:
343
+ line += theme.fg(ch, theme.CYAN)
344
+ elif v < 6:
345
+ line += theme.fg(ch, theme.PURPLE, bold=True)
346
+ else:
347
+ line += theme.fg(ch, theme.TEXT, bold=True)
348
+ rows.append(line)
349
+
350
+ info = [
351
+ theme.purple(f"Orbital {orbital_key}", bold=True) +
352
+ theme.dim(f" n={n} l={l} m={m} (Monte-Carlo |\u03c8|\u00b2 sampling)"),
353
+ theme.dim(f"Points placed: {placed}/{max_points}" + (" (paused)" if paused else "")),
354
+ ]
355
+ theme.cursor_home()
356
+ out.write(theme.panel(info + [""] + rows, title="orbital · live simulation",
357
+ color=theme.PURPLE, width=width + 8))
358
+ out.write("\n" + theme.faint(" [q] quit [space] pause [r] restart [s] snapshot") + "\n")
359
+ if status_msg:
360
+ out.write(" " + theme.green(status_msg) + "\n")
361
+ status_msg = ""
362
+ out.flush()
363
+ time.sleep(0.015)
364
+ except KeyboardInterrupt:
365
+ pass
366
+ finally:
367
+ theme.show_cursor()
368
+ print()