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,276 @@
1
+ """
2
+ Universal Chemistry Formula Calculator.
3
+
4
+ Two modes:
5
+
6
+ 1. Library mode — pick any of ~35 real chemistry formulas spanning
7
+ kinetics, gas laws, thermodynamics, electrochemistry, equilibrium,
8
+ solutions and atomic/quantum structure. Supply the values you know,
9
+ leave the one you want blank, and it's solved symbolically with
10
+ sympy for the missing variable — not hardcoded per-direction code.
11
+
12
+ 2. Free-form mode — type ANY formula (e.g. "P*V = n*R*T" or
13
+ "y = m*x + c") plus known variable=value pairs, and calc_terminal
14
+ solves for whatever's left, using real symbolic algebra.
15
+ """
16
+
17
+ try:
18
+ import sympy as sp
19
+ _HAS_SYMPY = True
20
+ except ImportError:
21
+ sp = None
22
+ _HAS_SYMPY = False
23
+
24
+ CONSTANTS = {
25
+ "R": 8.314, # J / (mol K)
26
+ "Rgas_atm": 0.0821, # L atm / (mol K)
27
+ "NA": 6.022e23, # Avogadro's number
28
+ "h": 6.626e-34, # Planck's constant, J s
29
+ "c": 3.0e8, # speed of light, m/s
30
+ "k_B": 1.381e-23, # Boltzmann constant
31
+ "F": 96500, # Faraday constant, C/mol
32
+ "e": 2.71828182845904523536,
33
+ "pi": 3.14159265358979323846,
34
+ "Rinf": 1.097e7, # Rydberg constant, 1/m
35
+ "a0": 5.29e-11, # Bohr radius, m
36
+ "me": 9.109e-31, # electron mass, kg
37
+ }
38
+
39
+ # Each entry: id -> (Display name, category, "lhs = rhs" string, {symbol: description})
40
+ FORMULA_LIBRARY = {
41
+ "kin_zero": ("Zero Order Kinetics", "Kinetics", "R = R0 - k*t",
42
+ {"R": "concentration at t", "R0": "initial concentration", "k": "rate constant", "t": "time"}),
43
+ "kin_first": ("First Order Rate Law", "Kinetics", "k = (2.303/t) * log(R0/R, 10)",
44
+ {"k": "rate constant", "t": "time", "R0": "initial concentration", "R": "concentration at t"}),
45
+ "kin_second": ("Second Order Kinetics", "Kinetics", "1/R = 1/R0 + k*t",
46
+ {"R": "concentration at t", "R0": "initial concentration", "k": "rate constant", "t": "time"}),
47
+ "kin_third": ("Third Order Kinetics", "Kinetics", "1/R**2 = 1/R0**2 + 2*k*t",
48
+ {"R": "concentration at t", "R0": "initial concentration", "k": "rate constant", "t": "time"}),
49
+ "half_life_1": ("First Order Half-Life", "Kinetics", "t_half = 0.693/k",
50
+ {"t_half": "half life", "k": "rate constant"}),
51
+ "half_life_0": ("Zero Order Half-Life", "Kinetics", "t_half = R0/(2*k)",
52
+ {"t_half": "half life", "R0": "initial concentration", "k": "rate constant"}),
53
+ "half_life_2": ("Second Order Half-Life", "Kinetics", "t_half = 1/(k*R0)",
54
+ {"t_half": "half life", "k": "rate constant", "R0": "initial concentration"}),
55
+ "arrhenius": ("Arrhenius Equation", "Kinetics", "k = A * exp(-Ea/(R*T))",
56
+ {"k": "rate constant", "A": "pre-exponential factor", "Ea": "activation energy (J/mol)",
57
+ "R": "gas constant", "T": "temperature (K)"}),
58
+ "ideal_gas": ("Ideal Gas Law", "Gas Laws", "P*V = n*R*T",
59
+ {"P": "pressure", "V": "volume", "n": "moles", "R": "gas constant", "T": "temperature (K)"}),
60
+ "boyles": ("Boyle's Law", "Gas Laws", "P1*V1 = P2*V2",
61
+ {"P1": "initial pressure", "V1": "initial volume", "P2": "final pressure", "V2": "final volume"}),
62
+ "charles": ("Charles's Law", "Gas Laws", "V1/T1 = V2/T2",
63
+ {"V1": "initial volume", "T1": "initial temp (K)", "V2": "final volume", "T2": "final temp (K)"}),
64
+ "gay_lussac": ("Gay-Lussac's Law", "Gas Laws", "P1/T1 = P2/T2",
65
+ {"P1": "initial pressure", "T1": "initial temp (K)", "P2": "final pressure", "T2": "final temp (K)"}),
66
+ "combined_gas": ("Combined Gas Law", "Gas Laws", "(P1*V1)/T1 = (P2*V2)/T2",
67
+ {"P1": "P initial", "V1": "V initial", "T1": "T initial", "P2": "P final", "V2": "V final", "T2": "T final"}),
68
+ "van_der_waals": ("Van der Waals Equation", "Gas Laws", "(P + a*n**2/V**2) * (V - n*b) = n*R*T",
69
+ {"P": "pressure", "a": "vdW constant a", "n": "moles", "V": "volume", "b": "vdW constant b",
70
+ "R": "gas constant", "T": "temperature"}),
71
+ "molarity": ("Molarity", "Solutions", "M = n/V",
72
+ {"M": "molarity (mol/L)", "n": "moles of solute", "V": "volume of solution (L)"}),
73
+ "moles_mass": ("Moles from Mass", "Mole Concept", "n = m/Mm",
74
+ {"n": "moles", "m": "mass (g)", "Mm": "molar mass (g/mol)"}),
75
+ "dilution": ("Dilution Law", "Solutions", "C1*V1 = C2*V2",
76
+ {"C1": "initial conc.", "V1": "initial volume", "C2": "final conc.", "V2": "final volume"}),
77
+ "ph": ("pH from [H+]", "Equilibrium/Acid-Base", "pH = -log(H, 10)",
78
+ {"pH": "pH", "H": "[H+] concentration (mol/L)"}),
79
+ "poh": ("pOH from [OH-]", "Equilibrium/Acid-Base", "pOH = -log(OH, 10)",
80
+ {"pOH": "pOH", "OH": "[OH-] concentration (mol/L)"}),
81
+ "ph_poh": ("pH + pOH = 14", "Equilibrium/Acid-Base", "pH + pOH = 14",
82
+ {"pH": "pH", "pOH": "pOH"}),
83
+ "henderson": ("Henderson-Hasselbalch", "Equilibrium/Acid-Base", "pH = pKa + log(A/HA, 10)",
84
+ {"pH": "pH", "pKa": "acid dissociation constant (pKa)", "A": "[conjugate base]", "HA": "[acid]"}),
85
+ "kw": ("Ion Product of Water", "Equilibrium/Acid-Base", "H*OH = 1.0e-14",
86
+ {"H": "[H+]", "OH": "[OH-]"}),
87
+ "nernst": ("Nernst Equation", "Electrochemistry", "Ecell = Ecell0 - (0.0592/n) * log(Q, 10)",
88
+ {"Ecell": "cell potential", "Ecell0": "standard cell potential", "n": "electrons transferred", "Q": "reaction quotient"}),
89
+ "faraday": ("Faraday's Law of Electrolysis", "Electrochemistry", "m = (M*I*t)/(n*F)",
90
+ {"m": "mass deposited (g)", "M": "molar mass", "I": "current (A)", "t": "time (s)",
91
+ "n": "electrons transferred", "F": "Faraday constant"}),
92
+ "gibbs": ("Gibbs Free Energy", "Thermodynamics", "dG = dH - T*dS",
93
+ {"dG": "Gibbs free energy change", "dH": "enthalpy change", "T": "temperature (K)", "dS": "entropy change"}),
94
+ "gibbs_cell": ("Gibbs Energy <-> Cell Potential", "Thermodynamics", "dG = -n*F*Ecell",
95
+ {"dG": "Gibbs free energy change", "n": "electrons transferred", "F": "Faraday constant", "Ecell": "cell potential"}),
96
+ "eq_gibbs": ("Gibbs Energy <-> Equilibrium Const.", "Thermodynamics", "dG0 = -R*T*log(K)",
97
+ {"dG0": "standard Gibbs free energy", "R": "gas constant", "T": "temperature (K)", "K": "equilibrium constant"}),
98
+ "clausius": ("Clausius-Clapeyron Equation", "Thermodynamics",
99
+ "log(P2/P1) = -(dHvap/R) * (1/T2 - 1/T1)",
100
+ {"P2": "vapor pressure at T2", "P1": "vapor pressure at T1", "dHvap": "enthalpy of vaporization",
101
+ "R": "gas constant", "T2": "temp 2 (K)", "T1": "temp 1 (K)"}),
102
+ "raoult": ("Raoult's Law", "Solutions", "Psoln = Xsolvent * P0",
103
+ {"Psoln": "vapor pressure of solution", "Xsolvent": "mole fraction of solvent", "P0": "pure solvent vapor pressure"}),
104
+ "osmotic": ("Osmotic Pressure", "Solutions", "Pi = M*R*T",
105
+ {"Pi": "osmotic pressure", "M": "molarity", "R": "gas constant", "T": "temperature (K)"}),
106
+ "freezing_dep": ("Freezing Point Depression", "Solutions", "dTf = Kf*m*i",
107
+ {"dTf": "freezing point depression", "Kf": "cryoscopic constant", "m": "molality", "i": "van't Hoff factor"}),
108
+ "boiling_elev": ("Boiling Point Elevation", "Solutions", "dTb = Kb*m*i",
109
+ {"dTb": "boiling point elevation", "Kb": "ebullioscopic constant", "m": "molality", "i": "van't Hoff factor"}),
110
+ "planck": ("Planck's Energy Equation", "Atomic/Quantum", "E = h*f",
111
+ {"E": "photon energy (J)", "h": "Planck's constant", "f": "frequency (Hz)"}),
112
+ "photon_wave": ("Photon Energy from Wavelength", "Atomic/Quantum", "E = (h*c)/lam",
113
+ {"E": "photon energy (J)", "h": "Planck's constant", "c": "speed of light", "lam": "wavelength (m)"}),
114
+ "de_broglie": ("De Broglie Wavelength", "Atomic/Quantum", "lam = h/(m*v)",
115
+ {"lam": "wavelength", "h": "Planck's constant", "m": "mass (kg)", "v": "velocity (m/s)"}),
116
+ "rydberg": ("Rydberg Equation", "Atomic/Quantum", "invlam = Rinf*(1/n1**2 - 1/n2**2)",
117
+ {"invlam": "1/wavelength (1/m)", "Rinf": "Rydberg constant", "n1": "lower level", "n2": "higher level"}),
118
+ "bohr_energy": ("Bohr Energy Level", "Atomic/Quantum", "En = -2.18e-18/n**2",
119
+ {"En": "orbit energy (J)", "n": "principal quantum number"}),
120
+ "bohr_radius": ("Bohr Radius of Orbit", "Atomic/Quantum", "rn = a0*n**2/Z",
121
+ {"rn": "orbit radius (m)", "a0": "Bohr radius const.", "n": "principal quantum number", "Z": "atomic number"}),
122
+ "heisenberg": ("Heisenberg Uncertainty Principle", "Atomic/Quantum", "dx*dp = h/(4*pi)",
123
+ {"dx": "position uncertainty", "dp": "momentum uncertainty", "h": "Planck's constant", "pi": "pi"}),
124
+ "avg_ke_gas": ("Average KE of Gas Molecule", "Thermodynamics", "KE = (3/2)*k_B*T",
125
+ {"KE": "average kinetic energy", "k_B": "Boltzmann constant", "T": "temperature (K)"}),
126
+ }
127
+
128
+ CATEGORIES = sorted({v[1] for v in FORMULA_LIBRARY.values()})
129
+
130
+
131
+ class SolveError(Exception):
132
+ pass
133
+
134
+
135
+ def _make_symbols(names):
136
+ return {n: sp.Symbol(n, real=True) for n in names}
137
+
138
+
139
+ def solve_formula(formula_str, known, solve_for, extra_constants=None):
140
+ """
141
+ formula_str: "lhs = rhs" style string, e.g. "P*V = n*R*T"
142
+ known: dict of {symbol_name: numeric value} for everything EXCEPT solve_for
143
+ solve_for: name of the symbol to solve for
144
+ Returns: (numeric_result, sympy_equation_used)
145
+ """
146
+ if not _HAS_SYMPY:
147
+ raise SolveError("sympy is not installed. Run: pip install sympy --break-system-packages "
148
+ "(or 'pip install -r requirements.txt') to enable /solve.")
149
+ if "=" not in formula_str:
150
+ raise SolveError("Formula must contain '=' (e.g. 'P*V = n*R*T').")
151
+ lhs_s, rhs_s = formula_str.split("=", 1)
152
+
153
+ const_ns = dict(CONSTANTS)
154
+ if extra_constants:
155
+ const_ns.update(extra_constants)
156
+
157
+ all_names = set(known) | {solve_for}
158
+ # discover any additional free symbols mentioned in the formula text
159
+ import re as _re
160
+ tokens = set(_re.findall(r"[A-Za-z_][A-Za-z_0-9]*", formula_str))
161
+ tokens -= {"log", "exp", "sqrt", "sin", "cos", "tan"}
162
+ all_names |= tokens
163
+
164
+ symtab = _make_symbols(all_names)
165
+ local_dict = dict(symtab)
166
+ local_dict.update({
167
+ "log": sp.log, "exp": sp.exp, "sqrt": sp.sqrt,
168
+ "sin": sp.sin, "cos": sp.cos, "tan": sp.tan,
169
+ })
170
+
171
+ try:
172
+ lhs = sp.sympify(lhs_s.strip(), locals=local_dict)
173
+ rhs = sp.sympify(rhs_s.strip(), locals=local_dict)
174
+ except Exception as exc:
175
+ raise SolveError(f"Could not parse formula: {exc}")
176
+
177
+ eq = sp.Eq(lhs, rhs)
178
+
179
+ # substitute known numeric values (and any constants the formula uses,
180
+ # e.g. R, h, c, NA, F, Rinf, a0, pi, e — unless the user overrides them)
181
+ subs = {}
182
+ for name, val in known.items():
183
+ if name in symtab:
184
+ subs[symtab[name]] = val
185
+ for name, val in const_ns.items():
186
+ if name in symtab and name not in known and name != solve_for:
187
+ subs[symtab[name]] = val
188
+
189
+ eq_sub = eq.subs(subs)
190
+
191
+ target = symtab[solve_for]
192
+ if target not in eq_sub.free_symbols:
193
+ raise SolveError(f"'{solve_for}' does not appear in this formula, or all values were already substituted.")
194
+
195
+ solutions = sp.solve(eq_sub, target)
196
+ if not solutions:
197
+ raise SolveError("No solution found — check the values you supplied.")
198
+
199
+ numeric = []
200
+ for s in solutions:
201
+ try:
202
+ numeric.append(complex(sp.N(s)))
203
+ except Exception:
204
+ continue
205
+ if not numeric:
206
+ raise SolveError("Solution is symbolic / non-numeric — supply more known values.")
207
+
208
+ # prefer the real, positive root when several exist (typical in chemistry)
209
+ real_candidates = [z.real for z in numeric if abs(z.imag) < 1e-9]
210
+ if real_candidates:
211
+ positives = [v for v in real_candidates if v > 0]
212
+ result = positives[0] if positives else real_candidates[0]
213
+ else:
214
+ result = numeric[0]
215
+
216
+ return result, eq
217
+
218
+
219
+ def pretty(obj):
220
+ """Render a formula string (e.g. 'P*V = n*R*T') or a sympy Eq/expr as
221
+ notebook-style text: division becomes a stacked fraction bar (never
222
+ '/'), multiplication becomes 'x' (never '*'). Falls back to a plain
223
+ text substitution if sympy isn't available or parsing fails.
224
+ """
225
+ def _fallback(s):
226
+ s = str(s).replace("**", "^")
227
+ # collapse "a*b*c" -> "a x b x c" without touching '**' (already gone)
228
+ return " x ".join(p.strip() for p in s.split("*"))
229
+
230
+ if not _HAS_SYMPY:
231
+ return _fallback(obj)
232
+
233
+ try:
234
+ if isinstance(obj, str):
235
+ import re as _re
236
+ if "=" in obj:
237
+ lhs_s, rhs_s = obj.split("=", 1)
238
+ tokens = set(_re.findall(r"[A-Za-z_][A-Za-z_0-9]*", obj))
239
+ tokens -= {"log", "exp", "sqrt", "sin", "cos", "tan"}
240
+ local_dict = dict(_make_symbols(tokens))
241
+ local_dict.update({
242
+ "log": sp.log, "exp": sp.exp, "sqrt": sp.sqrt,
243
+ "sin": sp.sin, "cos": sp.cos, "tan": sp.tan,
244
+ })
245
+ expr = sp.Eq(sp.sympify(lhs_s.strip(), locals=local_dict),
246
+ sp.sympify(rhs_s.strip(), locals=local_dict))
247
+ else:
248
+ expr = sp.sympify(obj)
249
+ else:
250
+ expr = obj
251
+ out = sp.pretty(expr, use_unicode=True)
252
+ except Exception:
253
+ return _fallback(obj)
254
+
255
+ # sympy's pretty-printer uses "\u22c5" (dot) for multiplication; the
256
+ # house style here is "x", never "*" or a bare dot.
257
+ out = out.replace("\u22c5", " x ")
258
+ return out
259
+
260
+
261
+ def solve_library_formula(key, known):
262
+ """Solve a named formula from FORMULA_LIBRARY. `known` must contain
263
+ every variable except exactly one (the one being solved for)."""
264
+ if key not in FORMULA_LIBRARY:
265
+ raise SolveError(f"Unknown formula id: {key}")
266
+ name, category, formula_str, var_desc = FORMULA_LIBRARY[key]
267
+ all_vars = set(var_desc)
268
+ missing = all_vars - set(known)
269
+ if len(missing) != 1:
270
+ raise SolveError(
271
+ f"Give values for exactly all-but-one variable. "
272
+ f"Variables: {', '.join(sorted(all_vars))}. Missing/target: {', '.join(missing) or 'none'}"
273
+ )
274
+ solve_for = next(iter(missing))
275
+ result, eq = solve_formula(formula_str, known, solve_for)
276
+ return solve_for, result, name, category, eq
calc_terminal/sound.py ADDED
@@ -0,0 +1,127 @@
1
+ """
2
+ Terminal sound effects for CCT — dependency-free, cross-platform.
3
+
4
+ No audio libraries are required. On Windows, the built-in `winsound`
5
+ module plays real short tones. Everywhere else (macOS/Linux terminals,
6
+ and Windows too if winsound is unavailable for some reason) it falls
7
+ back to the ASCII BEL character (`\\a`), which every terminal emulator
8
+ interprets as "ring the system bell" — the same mechanism your shell
9
+ uses for tab-complete errors. Some terminals mute this by default; if
10
+ nothing is audible, check your terminal's preferences for "audible
11
+ bell" / "terminal bell" and make sure it's turned on.
12
+
13
+ This module never raises and never blocks noticeably — a sound cue is
14
+ a nice-to-have, not something that should ever break the app or stall
15
+ an animation.
16
+ """
17
+
18
+ import sys
19
+ import threading
20
+ import time
21
+
22
+ try:
23
+ import winsound
24
+ _HAS_WINSOUND = True
25
+ except ImportError:
26
+ winsound = None
27
+ _HAS_WINSOUND = False
28
+
29
+ _ENABLED = True
30
+
31
+
32
+ def set_enabled(value):
33
+ global _ENABLED
34
+ _ENABLED = bool(value)
35
+
36
+
37
+ def is_enabled():
38
+ return _ENABLED
39
+
40
+
41
+ def _bell(times=1, gap=0.08):
42
+ for i in range(times):
43
+ sys.stdout.write("\a")
44
+ sys.stdout.flush()
45
+ if i < times - 1:
46
+ time.sleep(gap)
47
+
48
+
49
+ def _tone(freq, duration_ms):
50
+ """Try a real tone via winsound. Returns True if it actually played
51
+ a tone (so callers know NOT to also fall back to a bell)."""
52
+ if not _HAS_WINSOUND:
53
+ return False
54
+ try:
55
+ winsound.Beep(int(freq), int(duration_ms))
56
+ return True
57
+ except Exception:
58
+ return False
59
+
60
+
61
+ def play_async(event="notify"):
62
+ """v0.7.9.0 (requirement #1/#31 — SPEED): `winsound.Beep` BLOCKS its
63
+ calling thread for the full tone duration (~45-160 ms per tone, and
64
+ 'success' plays TWO). Inside the agent loop that meant every single
65
+ tool call paid 100-220 ms of pure sound latency — measured, not
66
+ guessed (see scripts/benchmark_cat.py: tool 'calculate' took ~219 ms,
67
+ all of it winsound.Beep). Sounds are now fired on a throwaway daemon
68
+ thread so the request path never waits on audio."""
69
+ if not _ENABLED:
70
+ return
71
+ threading.Thread(target=play, args=(event,), daemon=True).start()
72
+
73
+
74
+ # ---------------------------------------------------------------- events --
75
+ # Each named event below is used consistently across the app: booting up,
76
+ # a notebook/solve/plot finishing successfully, an error, a live
77
+ # simulation starting, and a quiet tick for each agent tool call so an
78
+ # agent run happening on-screen is also audible turn-by-turn.
79
+ def _play_blocking(event="notify"):
80
+ """The actual tone/bell sequence. Runs on a worker thread via play()
81
+ so no request/tool path ever waits on audio hardware."""
82
+ try:
83
+ if event == "startup":
84
+ if not _tone(660, 90):
85
+ _bell(1)
86
+ else:
87
+ _tone(880, 110)
88
+ elif event == "success":
89
+ if not _tone(880, 90):
90
+ _bell(1)
91
+ else:
92
+ _tone(1175, 120)
93
+ elif event == "error":
94
+ if not _tone(220, 160):
95
+ _bell(2, gap=0.12)
96
+ elif event == "sim_start":
97
+ if not _tone(600, 60):
98
+ _bell(1)
99
+ elif event == "tool":
100
+ # Deliberately quiet: a short tick per agent tool call, and
101
+ # NO bell fallback (repeated bells during a multi-step agent
102
+ # run would get annoying fast on terminals without winsound).
103
+ _tone(520, 45)
104
+ else:
105
+ _bell(1)
106
+ except Exception:
107
+ pass
108
+
109
+
110
+ def play(event="notify"):
111
+ """v0.7.9.0 SPEED FIX (measured, see scripts/benchmark_cat.py):
112
+ winsound.Beep BLOCKS its thread for the whole tone — 'success' costs
113
+ ~220 ms, every agent 'tool' tick ~45 ms. Playing inline meant every
114
+ single tool execution in a run paid that latency in wall-clock time.
115
+ Tones now run on a short-lived daemon thread; the ASCII-bell fallback
116
+ stays inline because writing '\\a' is instantaneous."""
117
+ if not _ENABLED:
118
+ return
119
+ if _HAS_WINSOUND:
120
+ threading.Thread(target=_play_blocking, args=(event,),
121
+ daemon=True).start()
122
+ else:
123
+ _play_blocking(event)
124
+
125
+
126
+ # Back-compat explicit-async alias (same behavior as play() now).
127
+ play_async = play