quantum-framework 0.18.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 (333) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/admin.py +131 -0
  4. quantum/cli/check.py +223 -0
  5. quantum/cli/commands/__init__.py +15 -0
  6. quantum/cli/commands/build.py +414 -0
  7. quantum/cli/commands/dev.py +185 -0
  8. quantum/cli/commands/docs.py +329 -0
  9. quantum/cli/commands/lint.py +523 -0
  10. quantum/cli/commands/migrate.py +548 -0
  11. quantum/cli/commands/new.py +622 -0
  12. quantum/cli/commands/serve.py +190 -0
  13. quantum/cli/commands/test.py +193 -0
  14. quantum/cli/deploy.py +810 -0
  15. quantum/cli/hot_reload.py +951 -0
  16. quantum/cli/jobs.py +356 -0
  17. quantum/cli/mq.py +582 -0
  18. quantum/cli/pkg.py +390 -0
  19. quantum/cli/runner.py +565 -0
  20. quantum/cli/server_process.py +159 -0
  21. quantum/cli/utils.py +334 -0
  22. quantum/compiler/__init__.py +30 -0
  23. quantum/compiler/base_generator.py +367 -0
  24. quantum/compiler/cli.py +295 -0
  25. quantum/compiler/expression_transformer.py +444 -0
  26. quantum/compiler/javascript/__init__.py +10 -0
  27. quantum/compiler/javascript/generator.py +659 -0
  28. quantum/compiler/optimizer.py +270 -0
  29. quantum/compiler/python/__init__.py +10 -0
  30. quantum/compiler/python/generator.py +883 -0
  31. quantum/compiler/python/runtime.py +863 -0
  32. quantum/compiler/transpiler.py +330 -0
  33. quantum/core/__init__.py +3 -0
  34. quantum/core/ast_nodes.py +2619 -0
  35. quantum/core/config_env.py +54 -0
  36. quantum/core/docs_links.py +8 -0
  37. quantum/core/expression_diagnostics.py +87 -0
  38. quantum/core/expression_stdlib.py +208 -0
  39. quantum/core/expressions.py +677 -0
  40. quantum/core/features/agents/src/__init__.py +30 -0
  41. quantum/core/features/agents/src/ast_node.py +540 -0
  42. quantum/core/features/conditionals/src/__init__.py +8 -0
  43. quantum/core/features/conditionals/src/ast_node.py +68 -0
  44. quantum/core/features/data_import/src/__init__.py +0 -0
  45. quantum/core/features/data_import/src/ast_node.py +292 -0
  46. quantum/core/features/data_import/src/runtime.py +533 -0
  47. quantum/core/features/dump/src/__init__.py +12 -0
  48. quantum/core/features/dump/src/ast_node.py +106 -0
  49. quantum/core/features/dump/src/parser.py +61 -0
  50. quantum/core/features/dump/src/runtime.py +246 -0
  51. quantum/core/features/functions/src/__init__.py +8 -0
  52. quantum/core/features/functions/src/ast_node.py +149 -0
  53. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  54. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  55. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  56. quantum/core/features/invocation/src/__init__.py +0 -0
  57. quantum/core/features/invocation/src/ast_node.py +147 -0
  58. quantum/core/features/invocation/src/runtime.py +332 -0
  59. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  60. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  61. quantum/core/features/knowledge_base/src/parser.py +82 -0
  62. quantum/core/features/logging/src/__init__.py +12 -0
  63. quantum/core/features/logging/src/ast_node.py +111 -0
  64. quantum/core/features/logging/src/parser.py +50 -0
  65. quantum/core/features/logging/src/runtime.py +190 -0
  66. quantum/core/features/loops/src/__init__.py +8 -0
  67. quantum/core/features/loops/src/ast_node.py +60 -0
  68. quantum/core/features/query/src/__init__.py +0 -0
  69. quantum/core/features/query/src/query_validators.py +20 -0
  70. quantum/core/features/state_management/src/__init__.py +11 -0
  71. quantum/core/features/state_management/src/ast_node.py +121 -0
  72. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  73. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  74. quantum/core/features/terminal_engine/src/parser.py +361 -0
  75. quantum/core/features/testing_engine/src/__init__.py +41 -0
  76. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  77. quantum/core/features/testing_engine/src/parser.py +604 -0
  78. quantum/core/features/theming/src/__init__.py +48 -0
  79. quantum/core/features/theming/src/ast_node.py +137 -0
  80. quantum/core/features/theming/src/presets.py +405 -0
  81. quantum/core/features/ui_engine/src/__init__.py +20 -0
  82. quantum/core/features/ui_engine/src/ast_nodes.py +1913 -0
  83. quantum/core/features/ui_engine/src/parser.py +1192 -0
  84. quantum/core/features/websocket/src/__init__.py +24 -0
  85. quantum/core/features/websocket/src/ast_node.py +247 -0
  86. quantum/core/guards.py +53 -0
  87. quantum/core/html_compat.py +299 -0
  88. quantum/core/parser.py +1551 -0
  89. quantum/core/parser_registry.py +213 -0
  90. quantum/core/parsers/__init__.py +76 -0
  91. quantum/core/parsers/ai/__init__.py +12 -0
  92. quantum/core/parsers/ai/agent_parser.py +106 -0
  93. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  94. quantum/core/parsers/ai/llm_parser.py +78 -0
  95. quantum/core/parsers/ai/team_parser.py +88 -0
  96. quantum/core/parsers/base.py +322 -0
  97. quantum/core/parsers/composition/__init__.py +10 -0
  98. quantum/core/parsers/composition/import_parser.py +50 -0
  99. quantum/core/parsers/composition/slot_parser.py +48 -0
  100. quantum/core/parsers/control_flow/__init__.py +11 -0
  101. quantum/core/parsers/control_flow/if_parser.py +71 -0
  102. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  103. quantum/core/parsers/control_flow/set_parser.py +93 -0
  104. quantum/core/parsers/data/__init__.py +12 -0
  105. quantum/core/parsers/data/data_parser.py +229 -0
  106. quantum/core/parsers/data/invoke_parser.py +133 -0
  107. quantum/core/parsers/data/query_parser.py +213 -0
  108. quantum/core/parsers/data/transaction_parser.py +86 -0
  109. quantum/core/parsers/events/__init__.py +9 -0
  110. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  111. quantum/core/parsers/forms/__init__.py +11 -0
  112. quantum/core/parsers/forms/action_parser.py +79 -0
  113. quantum/core/parsers/forms/flash_parser.py +34 -0
  114. quantum/core/parsers/forms/redirect_parser.py +33 -0
  115. quantum/core/parsers/functions/__init__.py +11 -0
  116. quantum/core/parsers/functions/function_parser.py +98 -0
  117. quantum/core/parsers/functions/param_parser.py +73 -0
  118. quantum/core/parsers/functions/return_parser.py +31 -0
  119. quantum/core/parsers/html/__init__.py +10 -0
  120. quantum/core/parsers/html/component_call_parser.py +130 -0
  121. quantum/core/parsers/html/html_parser.py +115 -0
  122. quantum/core/parsers/jobs/__init__.py +11 -0
  123. quantum/core/parsers/jobs/job_parser.py +71 -0
  124. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  125. quantum/core/parsers/jobs/thread_parser.py +57 -0
  126. quantum/core/parsers/messaging/__init__.py +17 -0
  127. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  128. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_parser.py +114 -0
  130. quantum/core/parsers/messaging/queue_parser.py +61 -0
  131. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  132. quantum/core/parsers/routing/__init__.py +9 -0
  133. quantum/core/parsers/routing/route_parser.py +41 -0
  134. quantum/core/parsers/scripting/__init__.py +12 -0
  135. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  136. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  137. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  138. quantum/core/parsers/scripting/python_parser.py +49 -0
  139. quantum/core/parsers/services/__init__.py +12 -0
  140. quantum/core/parsers/services/dump_parser.py +52 -0
  141. quantum/core/parsers/services/file_parser.py +48 -0
  142. quantum/core/parsers/services/log_parser.py +46 -0
  143. quantum/core/parsers/services/mail_parser.py +65 -0
  144. quantum/core/tiers.py +164 -0
  145. quantum/core/xml_lines.py +81 -0
  146. quantum/packages/__init__.py +28 -0
  147. quantum/packages/manager.py +413 -0
  148. quantum/packages/manifest.py +351 -0
  149. quantum/packages/registry.py +399 -0
  150. quantum/packages/resolver.py +336 -0
  151. quantum/plugins/__init__.py +33 -0
  152. quantum/plugins/hooks.py +329 -0
  153. quantum/plugins/loader.py +479 -0
  154. quantum/plugins/manifest.py +336 -0
  155. quantum/plugins/registry.py +371 -0
  156. quantum/runtime/__init__.py +25 -0
  157. quantum/runtime/action_handler.py +532 -0
  158. quantum/runtime/adapters/__init__.py +88 -0
  159. quantum/runtime/adapters/memory_adapter.py +690 -0
  160. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  161. quantum/runtime/adapters/redis_adapter.py +582 -0
  162. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  163. quantum/runtime/agent_service.py +1133 -0
  164. quantum/runtime/ast_cache.py +506 -0
  165. quantum/runtime/auth_service.py +267 -0
  166. quantum/runtime/component.py +1048 -0
  167. quantum/runtime/component_composer.py +133 -0
  168. quantum/runtime/component_resolver.py +174 -0
  169. quantum/runtime/conditions.py +54 -0
  170. quantum/runtime/database_service.py +595 -0
  171. quantum/runtime/db_schema.py +100 -0
  172. quantum/runtime/dev_panel.py +178 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +283 -0
  176. quantum/runtime/executor_registry.py +178 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +264 -0
  184. quantum/runtime/executors/control_flow/__init__.py +12 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +98 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +308 -0
  187. quantum/runtime/executors/control_flow/redirect_executor.py +43 -0
  188. quantum/runtime/executors/control_flow/set_executor.py +464 -0
  189. quantum/runtime/executors/data/__init__.py +12 -0
  190. quantum/runtime/executors/data/data_executor.py +156 -0
  191. quantum/runtime/executors/data/invoke_executor.py +228 -0
  192. quantum/runtime/executors/data/query_executor.py +290 -0
  193. quantum/runtime/executors/data/transaction_executor.py +91 -0
  194. quantum/runtime/executors/jobs/__init__.py +11 -0
  195. quantum/runtime/executors/jobs/job_executor.py +190 -0
  196. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  197. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  198. quantum/runtime/executors/messaging/__init__.py +17 -0
  199. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  200. quantum/runtime/executors/messaging/message_executor.py +174 -0
  201. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  202. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  203. quantum/runtime/executors/scripting/__init__.py +11 -0
  204. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  205. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  206. quantum/runtime/executors/scripting/python_executor.py +249 -0
  207. quantum/runtime/executors/services/__init__.py +12 -0
  208. quantum/runtime/executors/services/dump_executor.py +72 -0
  209. quantum/runtime/executors/services/file_executor.py +89 -0
  210. quantum/runtime/executors/services/log_executor.py +77 -0
  211. quantum/runtime/executors/services/mail_executor.py +81 -0
  212. quantum/runtime/expression_cache.py +498 -0
  213. quantum/runtime/file_upload_service.py +326 -0
  214. quantum/runtime/function_registry.py +118 -0
  215. quantum/runtime/game_builder.py +166 -0
  216. quantum/runtime/game_code_generator.py +2371 -0
  217. quantum/runtime/game_templates.py +2006 -0
  218. quantum/runtime/godot_code_generator.py +4681 -0
  219. quantum/runtime/godot_templates.py +1449 -0
  220. quantum/runtime/job_executor.py +1599 -0
  221. quantum/runtime/knowledge_service.py +546 -0
  222. quantum/runtime/llm_cache.py +100 -0
  223. quantum/runtime/llm_providers.py +704 -0
  224. quantum/runtime/llm_service.py +287 -0
  225. quantum/runtime/local_server.py +28 -0
  226. quantum/runtime/logging_setup.py +141 -0
  227. quantum/runtime/message_broker.py +364 -0
  228. quantum/runtime/message_queue_service.py +571 -0
  229. quantum/runtime/param_validation.py +218 -0
  230. quantum/runtime/pypy_compat.py +317 -0
  231. quantum/runtime/python_bridge.py +698 -0
  232. quantum/runtime/query_validators.py +304 -0
  233. quantum/runtime/renderer.py +734 -0
  234. quantum/runtime/service_container.py +470 -0
  235. quantum/runtime/table_params.py +163 -0
  236. quantum/runtime/terminal_builder.py +76 -0
  237. quantum/runtime/terminal_code_generator.py +607 -0
  238. quantum/runtime/terminal_templates.py +243 -0
  239. quantum/runtime/testing_builder.py +77 -0
  240. quantum/runtime/testing_code_generator.py +833 -0
  241. quantum/runtime/testing_templates.py +85 -0
  242. quantum/runtime/ui_builder.py +222 -0
  243. quantum/runtime/ui_console.py +405 -0
  244. quantum/runtime/ui_desktop.py +56 -0
  245. quantum/runtime/ui_form_rules.py +193 -0
  246. quantum/runtime/ui_html_adapter.py +2567 -0
  247. quantum/runtime/ui_html_templates.py +2106 -0
  248. quantum/runtime/ui_mobile_adapter.py +1832 -0
  249. quantum/runtime/ui_mobile_templates.py +1003 -0
  250. quantum/runtime/ui_page_renderer.py +392 -0
  251. quantum/runtime/ui_pager.py +73 -0
  252. quantum/runtime/ui_table_edit.py +139 -0
  253. quantum/runtime/ui_textual_adapter.py +1866 -0
  254. quantum/runtime/ui_textual_templates.py +45 -0
  255. quantum/runtime/ui_tokens.py +465 -0
  256. quantum/runtime/ui_validator.py +365 -0
  257. quantum/runtime/ui_view.py +221 -0
  258. quantum/runtime/validators.py +256 -0
  259. quantum/runtime/web_server.py +2131 -0
  260. quantum/runtime/websocket_adapter.py +501 -0
  261. quantum/runtime/websocket_service.py +585 -0
  262. quantum/runtime/websocket_transport.py +289 -0
  263. quantum/runtime/wsgi.py +101 -0
  264. quantum/services.py +109 -0
  265. quantum/utils/__init__.py +1 -0
  266. quantum_admin/__init__.py +4 -0
  267. quantum_admin/assets/quantum-admin-connectors.js +123 -0
  268. quantum_admin/assets/quantum-admin.css +2058 -0
  269. quantum_admin/assets/quantum-source-highlight.js +106 -0
  270. quantum_admin/backend/__init__.py +4 -0
  271. quantum_admin/backend/audit_service.py +346 -0
  272. quantum_admin/backend/auth_service.py +394 -0
  273. quantum_admin/backend/component_discovery.py +766 -0
  274. quantum_admin/backend/config_generator.py +586 -0
  275. quantum_admin/backend/connector_service.py +1016 -0
  276. quantum_admin/backend/crud.py +557 -0
  277. quantum_admin/backend/database.py +226 -0
  278. quantum_admin/backend/db_setup_service.py +193 -0
  279. quantum_admin/backend/deploy_service.py +698 -0
  280. quantum_admin/backend/docker_service.py +422 -0
  281. quantum_admin/backend/environment_service.py +418 -0
  282. quantum_admin/backend/git_service.py +370 -0
  283. quantum_admin/backend/health_service.py +390 -0
  284. quantum_admin/backend/job_service.py +347 -0
  285. quantum_admin/backend/log_service.py +331 -0
  286. quantum_admin/backend/main.py +14426 -0
  287. quantum_admin/backend/models.py +986 -0
  288. quantum_admin/backend/pipeline_service.py +812 -0
  289. quantum_admin/backend/resource_manager.py +1118 -0
  290. quantum_admin/backend/schemas.py +419 -0
  291. quantum_admin/backend/secret_manager.py +277 -0
  292. quantum_admin/backend/settings_service.py +488 -0
  293. quantum_admin/backend/template_service.py +710 -0
  294. quantum_admin/backend/test_execution_service.py +364 -0
  295. quantum_admin/backend/test_generator.py +782 -0
  296. quantum_admin/backend/webhook_service.py +454 -0
  297. quantum_admin/backend/websocket_manager.py +360 -0
  298. quantum_admin/components/admin/_layout/AdminShell.q +121 -0
  299. quantum_admin/components/admin/agents.q +107 -0
  300. quantum_admin/components/admin/app/[name].q +558 -0
  301. quantum_admin/components/admin/applications.q +160 -0
  302. quantum_admin/components/admin/component/[...path].q +205 -0
  303. quantum_admin/components/admin/components.q +59 -0
  304. quantum_admin/components/admin/connectors.q +283 -0
  305. quantum_admin/components/admin/dashboard.q +86 -0
  306. quantum_admin/components/admin/database.q +95 -0
  307. quantum_admin/components/admin/features.q +69 -0
  308. quantum_admin/components/admin/index.q +12 -0
  309. quantum_admin/components/admin/jobs.q +75 -0
  310. quantum_admin/components/admin/login.q +65 -0
  311. quantum_admin/components/admin/logout.q +11 -0
  312. quantum_admin/components/admin/settings.q +174 -0
  313. quantum_admin/components/admin/source.q +45 -0
  314. quantum_admin/components/admin/tests.q +73 -0
  315. quantum_admin/run.py +13 -0
  316. quantum_admin/services/__init__.py +16 -0
  317. quantum_admin/services/_base.py +84 -0
  318. quantum_admin/services/_component_test_generator.py +205 -0
  319. quantum_admin/services/_yaml_editor.py +82 -0
  320. quantum_admin/services/apps.py +330 -0
  321. quantum_admin/services/auth.py +61 -0
  322. quantum_admin/services/components.py +210 -0
  323. quantum_admin/services/connectors.py +164 -0
  324. quantum_admin/services/projects.py +189 -0
  325. quantum_admin/services/repository.py +223 -0
  326. quantum_admin/services/settings.py +150 -0
  327. quantum_admin/services/yaml_import.py +118 -0
  328. quantum_framework-0.18.0.dist-info/METADATA +281 -0
  329. quantum_framework-0.18.0.dist-info/RECORD +333 -0
  330. quantum_framework-0.18.0.dist-info/WHEEL +5 -0
  331. quantum_framework-0.18.0.dist-info/entry_points.txt +2 -0
  332. quantum_framework-0.18.0.dist-info/licenses/LICENSE +21 -0
  333. quantum_framework-0.18.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,85 @@
1
+ """
2
+ Testing Engine - Python Code Templates
3
+
4
+ Provides:
5
+ - TEST_TEMPLATE: Base template for generated pytest files
6
+ - Reuses PyBuilder from terminal_templates
7
+ """
8
+
9
+ from quantum.runtime.terminal_templates import PyBuilder, py_string, py_id
10
+
11
+
12
+ # ==========================================================================
13
+ # Test file template
14
+ # ==========================================================================
15
+
16
+ TEST_TEMPLATE = '''"""
17
+ {title} - Generated by Quantum Testing Engine
18
+ Run: pytest {filename} -v
19
+ Requires: pip install pytest playwright pytest-playwright
20
+ """
21
+
22
+ import pytest
23
+ import json
24
+ import time
25
+ from unittest.mock import MagicMock, patch
26
+ {extra_imports}
27
+
28
+ {test_code}
29
+ '''
30
+
31
+ BROWSER_IMPORTS = """import re
32
+ from playwright.sync_api import Page, expect, BrowserContext
33
+ """
34
+
35
+ ASYNC_BROWSER_IMPORTS = """import asyncio
36
+ from playwright.async_api import async_playwright, Page, expect, BrowserContext
37
+ """
38
+
39
+
40
+ # ==========================================================================
41
+ # Helper code snippets embedded in generated tests
42
+ # ==========================================================================
43
+
44
+ FIXTURE_HELPER = '''
45
+ @pytest.fixture
46
+ def {name}({params}):
47
+ """{docstring}"""
48
+ {body}
49
+ '''
50
+
51
+ CONFTEST_TEMPLATE = '''"""
52
+ conftest.py - Generated by Quantum Testing Engine
53
+ """
54
+
55
+ import pytest
56
+ from playwright.sync_api import sync_playwright
57
+
58
+ {fixtures}
59
+ '''
60
+
61
+
62
+ def sanitize_test_name(name: str) -> str:
63
+ """Convert a human-readable test name to a valid Python identifier."""
64
+ result = name.lower()
65
+ result = result.replace(' ', '_').replace('-', '_').replace('.', '_')
66
+ result = ''.join(c for c in result if c.isalnum() or c == '_')
67
+ if result and result[0].isdigit():
68
+ result = '_' + result
69
+ return result
70
+
71
+
72
+ def sanitize_class_name(name: str) -> str:
73
+ """Convert a suite name to a valid Python class name."""
74
+ # Replace separators with spaces for splitting
75
+ normalized = name.replace('-', ' ').replace('_', ' ')
76
+ words = normalized.split()
77
+ # Capitalize first letter of each word, preserve rest of casing
78
+ result = ''.join(w[0].upper() + w[1:] if w else '' for w in words)
79
+ # Remove non-alphanumeric chars
80
+ result = ''.join(c for c in result if c.isalnum())
81
+ if result and result[0].isdigit():
82
+ result = 'Test' + result
83
+ if not result.startswith('Test'):
84
+ result = 'Test' + result
85
+ return result
@@ -0,0 +1,222 @@
1
+ """
2
+ UI Engine - Builder/Orchestrator
3
+
4
+ Orchestrates the compilation pipeline:
5
+ ApplicationNode (type="ui") -> extract UI nodes -> Adapter -> output file
6
+
7
+ Targets (UI-8):
8
+ - html: a standalone HTML/CSS page — layout only
9
+ - textual: a standalone Python Textual app — layout only
10
+ - mobile / react-native: React Native (LABORATORIO: it translates q:set and
11
+ q:function to JavaScript, with no stability promise)
12
+
13
+ The application's logic runs in pages: a <q:component> with ui:* served by
14
+ `quantum start`, drawn by `quantum console` and `quantum desktop` too. The
15
+ old desktop target (a pywebview app that translated the logic to a JS bridge)
16
+ was removed in 0.16: `quantum desktop` opens the pages in a window.
17
+
18
+ Usage:
19
+ builder = UIBuilder()
20
+ code = builder.build(app_node, target='html')
21
+ builder.build_to_file(app_node, target='textual', output_path='app.py')
22
+ builder.build_to_file(app_node, target='mobile', output_path='App.js')
23
+ """
24
+
25
+ from pathlib import Path
26
+ from typing import Optional, Dict, Any, List
27
+
28
+ from quantum.core.ast_nodes import ApplicationNode
29
+ from quantum.core.features.state_management.src.ast_node import SetNode
30
+ from quantum.core.features.functions.src.ast_node import FunctionNode
31
+ from quantum.core.features.ui_engine.src.ast_nodes import UIWindowNode, is_ui_node
32
+
33
+
34
+ class UIBuildError(Exception):
35
+ """Error during UI app build."""
36
+ pass
37
+
38
+
39
+ class UIBuilder:
40
+ """Builds multi-target UI output from a Quantum UI ApplicationNode."""
41
+
42
+ def build(self, app: ApplicationNode, target: str = 'html') -> str:
43
+ """Build output string from an ApplicationNode with type='ui'.
44
+
45
+ Args:
46
+ app: ApplicationNode with ui_windows and ui_children populated.
47
+ target: 'html', 'textual', or 'desktop'.
48
+
49
+ Returns:
50
+ Generated source code string.
51
+ """
52
+ if not isinstance(app, ApplicationNode) or getattr(app, 'app_type', None) != 'ui':
53
+ # UI-0: it used to fail with "'ComponentNode' object has no
54
+ # attribute 'app_id'".
55
+ raise UIBuildError(
56
+ 'a UI build needs <q:application type="ui">; this file is a '
57
+ f'<q:{"component" if not isinstance(app, ApplicationNode) else "application"}>. '
58
+ 'Wrap the ui:window in <q:application id="MyApp" type="ui">.')
59
+ windows = getattr(app, 'ui_windows', [])
60
+ ui_children = getattr(app, 'ui_children', [])
61
+ title = app.app_id or 'Quantum UI'
62
+
63
+ if not windows and not ui_children:
64
+ raise UIBuildError("No UI elements found in application")
65
+
66
+ # Get theme from application
67
+ theme = getattr(app, 'ui_theme', None)
68
+
69
+ if target == 'desktop':
70
+ raise UIBuildError(
71
+ '--target desktop was removed in Quantum 0.16: it translated the logic to a '
72
+ 'JavaScript bridge of its own. Write the screens as pages (<q:component> with '
73
+ 'ui:* in components/) and run `quantum desktop` — the same pages, in a window.')
74
+ if target in ('html', 'textual'):
75
+ self._layout_only(app, target)
76
+
77
+ if target == 'html':
78
+ from quantum.runtime.ui_html_adapter import UIHtmlAdapter
79
+ adapter = UIHtmlAdapter()
80
+ return adapter.generate(windows, ui_children, title, theme=theme)
81
+ elif target == 'textual':
82
+ from quantum.runtime.ui_textual_adapter import UITextualAdapter
83
+ adapter = UITextualAdapter()
84
+ return adapter.generate(windows, ui_children, title)
85
+ elif target in ('mobile', 'react-native'):
86
+ from quantum.core.tiers import warn_ui_target
87
+ from quantum.runtime.ui_mobile_adapter import UIReactNativeAdapter
88
+ warn_ui_target(target)
89
+ adapter = UIReactNativeAdapter()
90
+
91
+ # Extract functions and state for React Native hooks
92
+ functions = self._extract_functions(app)
93
+ state_vars = self._extract_state(app)
94
+
95
+ return adapter.generate(
96
+ windows, ui_children, title,
97
+ functions=functions,
98
+ state_vars=state_vars
99
+ )
100
+ else:
101
+ raise UIBuildError(f"Unknown target: {target}. Must be html, textual or mobile")
102
+
103
+ def build_to_file(self, app: ApplicationNode, target: str = 'html',
104
+ output_path: Optional[str] = None) -> str:
105
+ """Build and write output file. Returns the output file path."""
106
+ code = self.build(app, target)
107
+
108
+ if output_path is None:
109
+ # UI-0: one file per target. textual and desktop both wrote
110
+ # <id>.py, so building one erased the other.
111
+ sufixo = {'html': '.html', 'textual': '_console.py',
112
+ 'mobile': '.js', 'react-native': '.js'}.get(target, '.py')
113
+ output_path = f"{app.app_id}{sufixo}"
114
+
115
+ path = Path(output_path)
116
+ path.parent.mkdir(parents=True, exist_ok=True)
117
+ path.write_text(code, encoding='utf-8')
118
+
119
+ return str(path.resolve())
120
+
121
+ def _layout_only(self, app: ApplicationNode, target: str) -> None:
122
+ """UI-8: html and textual builds draw layout; logic in them is an error.
123
+
124
+ It used to be dropped without a word — `{count}` came out raw and a
125
+ button called a function that did not exist.
126
+ """
127
+ logica = [f'<q:function name="{f.name}">' for f in getattr(app, 'functions', [])]
128
+ logica += [f'<q:set name="{v.name}">' for v in getattr(app, 'state_vars', [])]
129
+
130
+ def andar(nos):
131
+ for no in nos or []:
132
+ if is_ui_node(no):
133
+ andar(getattr(no, 'children', None))
134
+ elif isinstance(no, (SetNode, FunctionNode)):
135
+ logica.append(f'<q:{"set" if isinstance(no, SetNode) else "function"} name="{no.name}">')
136
+ elif type(no).__name__ not in ('TextNode', 'HTMLNode', 'CommentNode'):
137
+ logica.append(f'<q:{type(no).__name__.replace("Node", "").lower()}>')
138
+
139
+ andar(getattr(app, 'ui_windows', []))
140
+ andar(getattr(app, 'ui_children', []))
141
+ if logica:
142
+ raise UIBuildError(
143
+ f'a {target} UI build draws layout only, and {logica[0]} would not run in it. '
144
+ 'Write the screen as a page — <q:component> with ui:* in components/ — '
145
+ 'and run it with `quantum start`, `quantum console` or `quantum desktop`, '
146
+ "where the page's runtime runs its logic.")
147
+
148
+ def _extract_functions(self, app: ApplicationNode) -> Dict[str, FunctionNode]:
149
+ """Extract q:function nodes from application.
150
+
151
+ Only extracts UI functions (those without REST endpoints),
152
+ as REST functions are for server-side use.
153
+
154
+ Args:
155
+ app: ApplicationNode to extract functions from.
156
+
157
+ Returns:
158
+ Dict mapping function name to FunctionNode.
159
+ """
160
+ functions = {}
161
+
162
+ # Check components for functions
163
+ for component in getattr(app, 'components', []):
164
+ for func in getattr(component, 'functions', []):
165
+ # Only include UI functions (not REST endpoints)
166
+ if not func.is_rest_enabled():
167
+ functions[func.name] = func
168
+
169
+ # Check for functions directly on app (if any)
170
+ for func in getattr(app, 'functions', []):
171
+ if not func.is_rest_enabled():
172
+ functions[func.name] = func
173
+
174
+ # Check ui_children for functions (when parsed directly into app)
175
+ for child in getattr(app, 'ui_children', []):
176
+ if isinstance(child, FunctionNode) and not child.is_rest_enabled():
177
+ functions[child.name] = child
178
+
179
+ # Check ui_windows children for functions
180
+ for window in getattr(app, 'ui_windows', []):
181
+ for child in getattr(window, 'children', []):
182
+ if isinstance(child, FunctionNode) and not child.is_rest_enabled():
183
+ functions[child.name] = child
184
+
185
+ return functions
186
+
187
+ def _extract_state(self, app: ApplicationNode) -> Dict[str, Any]:
188
+ """Extract q:set nodes from application level.
189
+
190
+ Extracts initial state variables defined at the application or
191
+ component level for state initialization.
192
+
193
+ Args:
194
+ app: ApplicationNode to extract state from.
195
+
196
+ Returns:
197
+ Dict mapping variable name to initial value.
198
+ """
199
+ state = {}
200
+
201
+ # Check ui_children for SetNode (top-level state declarations)
202
+ for child in getattr(app, 'ui_children', []):
203
+ if isinstance(child, SetNode):
204
+ # Use default if value is not set
205
+ value = child.value if child.value is not None else child.default
206
+ state[child.name] = value
207
+
208
+ # Check ui_windows children for SetNode
209
+ for window in getattr(app, 'ui_windows', []):
210
+ for child in getattr(window, 'children', []):
211
+ if isinstance(child, SetNode):
212
+ value = child.value if child.value is not None else child.default
213
+ state[child.name] = value
214
+
215
+ # Check components for statements that are SetNode
216
+ for component in getattr(app, 'components', []):
217
+ for stmt in getattr(component, 'statements', []):
218
+ if isinstance(stmt, SetNode):
219
+ value = stmt.value if stmt.value is not None else stmt.default
220
+ state[stmt.name] = value
221
+
222
+ return state