@typecad/cuttlefish 0.1.0-alpha.1

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 (377) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/api/board-types.d.ts +1 -0
  4. package/dist/api/board-types.js +4 -0
  5. package/dist/api/capabilities.d.ts +12 -0
  6. package/dist/api/capabilities.js +1 -0
  7. package/dist/api/config.d.ts +120 -0
  8. package/dist/api/config.js +7 -0
  9. package/dist/api/index.d.ts +5 -0
  10. package/dist/api/index.js +2 -0
  11. package/dist/api/schema/builder.d.ts +14 -0
  12. package/dist/api/schema/builder.js +15 -0
  13. package/dist/api/schema/index.d.ts +2 -0
  14. package/dist/api/schema/index.js +1 -0
  15. package/dist/api/schema/types.d.ts +257 -0
  16. package/dist/api/schema/types.js +4 -0
  17. package/dist/api/shared/async-runtime-static.d.ts +8 -0
  18. package/dist/api/shared/async-runtime-static.js +201 -0
  19. package/dist/api/shared/async-types.d.ts +42 -0
  20. package/dist/api/shared/async-types.js +8 -0
  21. package/dist/api/shared/board-resolver.d.ts +12 -0
  22. package/dist/api/shared/board-resolver.js +7 -0
  23. package/dist/api/shared/cpp-type-ir.d.ts +229 -0
  24. package/dist/api/shared/cpp-type-ir.js +672 -0
  25. package/dist/api/shared/display-adapter.d.ts +12 -0
  26. package/dist/api/shared/display-adapter.js +145 -0
  27. package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
  28. package/dist/api/shared/display-adapters/eink-mono.js +53 -0
  29. package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
  30. package/dist/api/shared/display-adapters/sdl.js +452 -0
  31. package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
  32. package/dist/api/shared/display-adapters/ssd1309.js +136 -0
  33. package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
  34. package/dist/api/shared/display-adapters/st7796.js +132 -0
  35. package/dist/api/shared/display-capabilities.d.ts +45 -0
  36. package/dist/api/shared/display-capabilities.js +54 -0
  37. package/dist/api/shared/display-op-ir.d.ts +62 -0
  38. package/dist/api/shared/display-op-ir.js +19 -0
  39. package/dist/api/shared/display-profile.d.ts +265 -0
  40. package/dist/api/shared/display-profile.js +338 -0
  41. package/dist/api/shared/graphics-strategy.d.ts +29 -0
  42. package/dist/api/shared/graphics-strategy.js +11 -0
  43. package/dist/api/shared/hal-op-ir.d.ts +393 -0
  44. package/dist/api/shared/hal-op-ir.js +18 -0
  45. package/dist/api/shared/index.d.ts +26 -0
  46. package/dist/api/shared/index.js +21 -0
  47. package/dist/api/shared/ir-core.d.ts +363 -0
  48. package/dist/api/shared/ir-core.js +8 -0
  49. package/dist/api/shared/ir-declarations.d.ts +201 -0
  50. package/dist/api/shared/ir-declarations.js +26 -0
  51. package/dist/api/shared/ir.d.ts +85 -0
  52. package/dist/api/shared/ir.js +13 -0
  53. package/dist/api/shared/platform-strategy.d.ts +402 -0
  54. package/dist/api/shared/platform-strategy.js +14 -0
  55. package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
  56. package/dist/api/shared/polyfill-helper-registry.js +86 -0
  57. package/dist/api/shared/polyfill-types.d.ts +33 -0
  58. package/dist/api/shared/polyfill-types.js +76 -0
  59. package/dist/api/shared/promise-runtime.d.ts +10 -0
  60. package/dist/api/shared/promise-runtime.js +244 -0
  61. package/dist/api/shared/snprintf-types.d.ts +29 -0
  62. package/dist/api/shared/snprintf-types.js +8 -0
  63. package/dist/api/shared/string-method-registry.d.ts +53 -0
  64. package/dist/api/shared/string-method-registry.js +168 -0
  65. package/dist/api/shared/toolchain-types.d.ts +63 -0
  66. package/dist/api/shared/toolchain-types.js +71 -0
  67. package/dist/api/shared/types.d.ts +50 -0
  68. package/dist/api/shared/types.js +6 -0
  69. package/dist/ast/parse.d.ts +2 -0
  70. package/dist/ast/parse.js +4 -0
  71. package/dist/cache.d.ts +26 -0
  72. package/dist/cache.js +95 -0
  73. package/dist/cli-utils.d.ts +37 -0
  74. package/dist/cli-utils.js +250 -0
  75. package/dist/cli.d.ts +2 -0
  76. package/dist/cli.js +635 -0
  77. package/dist/config-loader.d.ts +78 -0
  78. package/dist/config-loader.js +508 -0
  79. package/dist/config-schema.d.ts +233 -0
  80. package/dist/config-schema.js +83 -0
  81. package/dist/create/board-checklist.d.ts +2 -0
  82. package/dist/create/board-checklist.js +52 -0
  83. package/dist/create/board-codegen.d.ts +9 -0
  84. package/dist/create/board-codegen.js +238 -0
  85. package/dist/create/board-generators.d.ts +12 -0
  86. package/dist/create/board-generators.js +652 -0
  87. package/dist/create/board-spec.d.ts +640 -0
  88. package/dist/create/board-spec.js +215 -0
  89. package/dist/create/eslint-rules-template.d.ts +2 -0
  90. package/dist/create/eslint-rules-template.js +672 -0
  91. package/dist/create/index.d.ts +11 -0
  92. package/dist/create/index.js +8 -0
  93. package/dist/create/init-scaffold.d.ts +27 -0
  94. package/dist/create/init-scaffold.js +150 -0
  95. package/dist/create/init-templates.d.ts +24 -0
  96. package/dist/create/init-templates.js +379 -0
  97. package/dist/create/init-wizard.d.ts +8 -0
  98. package/dist/create/init-wizard.js +168 -0
  99. package/dist/debug/breakpoint-loader.d.ts +24 -0
  100. package/dist/debug/breakpoint-loader.js +137 -0
  101. package/dist/debug/index.d.ts +3 -0
  102. package/dist/debug/index.js +7 -0
  103. package/dist/debug/preprocessor.d.ts +9 -0
  104. package/dist/debug/preprocessor.js +214 -0
  105. package/dist/debug/types.d.ts +32 -0
  106. package/dist/debug/types.js +6 -0
  107. package/dist/diagnostics/diagnostics-report.d.ts +51 -0
  108. package/dist/diagnostics/diagnostics-report.js +333 -0
  109. package/dist/diagnostics/json-schema.d.ts +138 -0
  110. package/dist/diagnostics/json-schema.js +27 -0
  111. package/dist/diagnostics/md-writer.d.ts +10 -0
  112. package/dist/diagnostics/md-writer.js +336 -0
  113. package/dist/diagnostics/mermaid-builder.d.ts +65 -0
  114. package/dist/diagnostics/mermaid-builder.js +681 -0
  115. package/dist/emit/cpp-emitter.d.ts +6 -0
  116. package/dist/emit/cpp-emitter.js +48 -0
  117. package/dist/emit/emitters/class-emitter.d.ts +2 -0
  118. package/dist/emit/emitters/class-emitter.js +523 -0
  119. package/dist/emit/emitters/emitter-context.d.ts +174 -0
  120. package/dist/emit/emitters/emitter-context.js +1 -0
  121. package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
  122. package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
  123. package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
  124. package/dist/emit/emitters/function-emitter-impl.js +428 -0
  125. package/dist/emit/emitters/line-appender.d.ts +15 -0
  126. package/dist/emit/emitters/line-appender.js +344 -0
  127. package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
  128. package/dist/emit/emitters/namespace-emitter.js +324 -0
  129. package/dist/emit/emitters/output-finalizer.d.ts +4 -0
  130. package/dist/emit/emitters/output-finalizer.js +267 -0
  131. package/dist/emit/emitters/setup.d.ts +3 -0
  132. package/dist/emit/emitters/setup.js +851 -0
  133. package/dist/emit/emitters/top-level-prep.d.ts +2 -0
  134. package/dist/emit/emitters/top-level-prep.js +706 -0
  135. package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
  136. package/dist/emit/emitters/type-decl-emitter.js +251 -0
  137. package/dist/emit/emitters/ui-emitter.d.ts +21 -0
  138. package/dist/emit/emitters/ui-emitter.js +564 -0
  139. package/dist/emit/enum-emitter.d.ts +1 -0
  140. package/dist/emit/enum-emitter.js +1 -0
  141. package/dist/emit/expression-renderer.d.ts +283 -0
  142. package/dist/emit/expression-renderer.js +1477 -0
  143. package/dist/emit/native-helpers-emitter.d.ts +11 -0
  144. package/dist/emit/native-helpers-emitter.js +34 -0
  145. package/dist/emit/route-hal-op.d.ts +5 -0
  146. package/dist/emit/route-hal-op.js +13 -0
  147. package/dist/emit/snprintf-helpers.d.ts +17 -0
  148. package/dist/emit/snprintf-helpers.js +462 -0
  149. package/dist/emit/statement-renderer.d.ts +189 -0
  150. package/dist/emit/statement-renderer.js +948 -0
  151. package/dist/emit/utils/async-state-machine.d.ts +42 -0
  152. package/dist/emit/utils/async-state-machine.js +343 -0
  153. package/dist/emit/utils/comment-helpers.d.ts +20 -0
  154. package/dist/emit/utils/comment-helpers.js +31 -0
  155. package/dist/emit/utils/cpp-helpers.d.ts +47 -0
  156. package/dist/emit/utils/cpp-helpers.js +85 -0
  157. package/dist/emit/utils/include-resolver.d.ts +58 -0
  158. package/dist/emit/utils/include-resolver.js +155 -0
  159. package/dist/emit/utils/index.d.ts +8 -0
  160. package/dist/emit/utils/index.js +12 -0
  161. package/dist/emit/utils/type-inference.d.ts +159 -0
  162. package/dist/emit/utils/type-inference.js +559 -0
  163. package/dist/eslint-check.d.ts +10 -0
  164. package/dist/eslint-check.js +102 -0
  165. package/dist/framework-package.d.ts +10 -0
  166. package/dist/framework-package.js +58 -0
  167. package/dist/framework-registry.d.ts +39 -0
  168. package/dist/framework-registry.js +30 -0
  169. package/dist/incremental-cache.d.ts +116 -0
  170. package/dist/incremental-cache.js +335 -0
  171. package/dist/ir/adc-range-validation.d.ts +11 -0
  172. package/dist/ir/adc-range-validation.js +250 -0
  173. package/dist/ir/ast-node-utils.d.ts +8 -0
  174. package/dist/ir/ast-node-utils.js +32 -0
  175. package/dist/ir/board-pin-utils.d.ts +13 -0
  176. package/dist/ir/board-pin-utils.js +65 -0
  177. package/dist/ir/board-resolver.d.ts +36 -0
  178. package/dist/ir/board-resolver.js +437 -0
  179. package/dist/ir/build-ir-state.d.ts +167 -0
  180. package/dist/ir/build-ir-state.js +306 -0
  181. package/dist/ir/build-ir.d.ts +2 -0
  182. package/dist/ir/build-ir.js +808 -0
  183. package/dist/ir/call-graph.d.ts +35 -0
  184. package/dist/ir/call-graph.js +194 -0
  185. package/dist/ir/compile-time-only.d.ts +2 -0
  186. package/dist/ir/compile-time-only.js +16 -0
  187. package/dist/ir/declaration-builders.d.ts +9 -0
  188. package/dist/ir/declaration-builders.js +608 -0
  189. package/dist/ir/entry-points.d.ts +30 -0
  190. package/dist/ir/entry-points.js +191 -0
  191. package/dist/ir/expression-to-ir.d.ts +5 -0
  192. package/dist/ir/expression-to-ir.js +2249 -0
  193. package/dist/ir/extract-property-chain.d.ts +10 -0
  194. package/dist/ir/extract-property-chain.js +30 -0
  195. package/dist/ir/feature-prescan.d.ts +3 -0
  196. package/dist/ir/feature-prescan.js +43 -0
  197. package/dist/ir/feature-registry.d.ts +30 -0
  198. package/dist/ir/feature-registry.js +816 -0
  199. package/dist/ir/filter.d.ts +7 -0
  200. package/dist/ir/filter.js +85 -0
  201. package/dist/ir/function-builder.d.ts +19 -0
  202. package/dist/ir/function-builder.js +670 -0
  203. package/dist/ir/hal/hal-emitter.d.ts +62 -0
  204. package/dist/ir/hal/hal-emitter.js +735 -0
  205. package/dist/ir/hal/hal-parser.d.ts +61 -0
  206. package/dist/ir/hal/hal-parser.js +473 -0
  207. package/dist/ir/hal/hal-plugins.d.ts +34 -0
  208. package/dist/ir/hal/hal-plugins.js +706 -0
  209. package/dist/ir/hal-resolver.d.ts +5 -0
  210. package/dist/ir/hal-resolver.js +5 -0
  211. package/dist/ir/heap-analysis.d.ts +8 -0
  212. package/dist/ir/heap-analysis.js +350 -0
  213. package/dist/ir/heap-array-validation.d.ts +24 -0
  214. package/dist/ir/heap-array-validation.js +29 -0
  215. package/dist/ir/identifier-collector.d.ts +18 -0
  216. package/dist/ir/identifier-collector.js +439 -0
  217. package/dist/ir/interrupt-analysis.d.ts +39 -0
  218. package/dist/ir/interrupt-analysis.js +467 -0
  219. package/dist/ir/memory-budget-validation.d.ts +10 -0
  220. package/dist/ir/memory-budget-validation.js +86 -0
  221. package/dist/ir/namespace-builder.d.ts +7 -0
  222. package/dist/ir/namespace-builder.js +152 -0
  223. package/dist/ir/ownership-analysis.d.ts +9 -0
  224. package/dist/ir/ownership-analysis.js +1207 -0
  225. package/dist/ir/peripheral-ownership.d.ts +3 -0
  226. package/dist/ir/peripheral-ownership.js +152 -0
  227. package/dist/ir/peripheral-symbols.d.ts +7 -0
  228. package/dist/ir/peripheral-symbols.js +57 -0
  229. package/dist/ir/peripheral-usage.d.ts +54 -0
  230. package/dist/ir/peripheral-usage.js +691 -0
  231. package/dist/ir/peripheral-validation.d.ts +8 -0
  232. package/dist/ir/peripheral-validation.js +77 -0
  233. package/dist/ir/pin-alias-conflict.d.ts +4 -0
  234. package/dist/ir/pin-alias-conflict.js +34 -0
  235. package/dist/ir/pin-capability-validation.d.ts +6 -0
  236. package/dist/ir/pin-capability-validation.js +413 -0
  237. package/dist/ir/pin-mode-validation.d.ts +10 -0
  238. package/dist/ir/pin-mode-validation.js +301 -0
  239. package/dist/ir/pin-safety.d.ts +11 -0
  240. package/dist/ir/pin-safety.js +58 -0
  241. package/dist/ir/program-analysis.d.ts +44 -0
  242. package/dist/ir/program-analysis.js +589 -0
  243. package/dist/ir/pulldown-validation.d.ts +7 -0
  244. package/dist/ir/pulldown-validation.js +49 -0
  245. package/dist/ir/pwm-timer-sharing.d.ts +4 -0
  246. package/dist/ir/pwm-timer-sharing.js +93 -0
  247. package/dist/ir/reachability.d.ts +68 -0
  248. package/dist/ir/reachability.js +224 -0
  249. package/dist/ir/register-decorators.d.ts +6 -0
  250. package/dist/ir/register-decorators.js +49 -0
  251. package/dist/ir/render-expr.d.ts +4 -0
  252. package/dist/ir/render-expr.js +127 -0
  253. package/dist/ir/resource-analysis.d.ts +19 -0
  254. package/dist/ir/resource-analysis.js +153 -0
  255. package/dist/ir/statement-to-ir.d.ts +23 -0
  256. package/dist/ir/statement-to-ir.js +349 -0
  257. package/dist/ir/symbol-table.d.ts +68 -0
  258. package/dist/ir/symbol-table.js +179 -0
  259. package/dist/ir/symbol-types.d.ts +56 -0
  260. package/dist/ir/symbol-types.js +107 -0
  261. package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
  262. package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
  263. package/dist/ir/timing-validation.d.ts +7 -0
  264. package/dist/ir/timing-validation.js +95 -0
  265. package/dist/ir/transformers/array-methods.d.ts +8 -0
  266. package/dist/ir/transformers/array-methods.js +493 -0
  267. package/dist/ir/transformers/call-statement.d.ts +5 -0
  268. package/dist/ir/transformers/call-statement.js +758 -0
  269. package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
  270. package/dist/ir/transformers/callback-context-registry.js +55 -0
  271. package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
  272. package/dist/ir/transformers/canvas-lowering.js +249 -0
  273. package/dist/ir/transformers/control-flow.d.ts +8 -0
  274. package/dist/ir/transformers/control-flow.js +508 -0
  275. package/dist/ir/transformers/expressions.d.ts +7 -0
  276. package/dist/ir/transformers/expressions.js +406 -0
  277. package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
  278. package/dist/ir/transformers/hal-call-resolver.js +274 -0
  279. package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
  280. package/dist/ir/transformers/hal-emit-helpers.js +63 -0
  281. package/dist/ir/transformers/namespace-methods.d.ts +20 -0
  282. package/dist/ir/transformers/namespace-methods.js +110 -0
  283. package/dist/ir/transformers/register-assignment.d.ts +5 -0
  284. package/dist/ir/transformers/register-assignment.js +56 -0
  285. package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
  286. package/dist/ir/transformers/ui-call-resolver.js +1136 -0
  287. package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
  288. package/dist/ir/transformers/ui-callback-lowering.js +623 -0
  289. package/dist/ir/transformers/ui-mount.d.ts +20 -0
  290. package/dist/ir/transformers/ui-mount.js +35 -0
  291. package/dist/ir/transformers/ui-reactive.d.ts +68 -0
  292. package/dist/ir/transformers/ui-reactive.js +129 -0
  293. package/dist/ir/transformers/variables.d.ts +14 -0
  294. package/dist/ir/transformers/variables.js +1045 -0
  295. package/dist/ir/try-catch-validation.d.ts +26 -0
  296. package/dist/ir/try-catch-validation.js +128 -0
  297. package/dist/ir/type-resolution.d.ts +31 -0
  298. package/dist/ir/type-resolution.js +977 -0
  299. package/dist/ir/ui-element-auto-wire.d.ts +50 -0
  300. package/dist/ir/ui-element-auto-wire.js +218 -0
  301. package/dist/ir/unit-suspicion-validation.d.ts +12 -0
  302. package/dist/ir/unit-suspicion-validation.js +296 -0
  303. package/dist/ir/utils/walk-ir.d.ts +6 -0
  304. package/dist/ir/utils/walk-ir.js +162 -0
  305. package/dist/ir/validation-orchestrator.d.ts +4 -0
  306. package/dist/ir/validation-orchestrator.js +45 -0
  307. package/dist/libdef/base-class-resolver.d.ts +23 -0
  308. package/dist/libdef/base-class-resolver.js +50 -0
  309. package/dist/libdef/cpp-to-decl.d.ts +34 -0
  310. package/dist/libdef/cpp-to-decl.js +584 -0
  311. package/dist/libdef/header-parser.d.ts +53 -0
  312. package/dist/libdef/header-parser.js +269 -0
  313. package/dist/libdef/registry.d.ts +10 -0
  314. package/dist/libdef/registry.js +142 -0
  315. package/dist/mapping/peripheral-names.d.ts +23 -0
  316. package/dist/mapping/peripheral-names.js +63 -0
  317. package/dist/mapping/source-map.d.ts +46 -0
  318. package/dist/mapping/source-map.js +207 -0
  319. package/dist/orchestrator/dts-generator.d.ts +17 -0
  320. package/dist/orchestrator/dts-generator.js +83 -0
  321. package/dist/orchestrator/graph-builder.d.ts +18 -0
  322. package/dist/orchestrator/graph-builder.js +218 -0
  323. package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
  324. package/dist/orchestrator/semantic-facts-verifier.js +121 -0
  325. package/dist/orchestrator/semantic-facts.d.ts +141 -0
  326. package/dist/orchestrator/semantic-facts.js +652 -0
  327. package/dist/orchestrator/type-checker.d.ts +39 -0
  328. package/dist/orchestrator/type-checker.js +760 -0
  329. package/dist/platform/async-runtime.d.ts +8 -0
  330. package/dist/platform/async-runtime.js +26 -0
  331. package/dist/platform/generic-strategy.d.ts +101 -0
  332. package/dist/platform/generic-strategy.js +289 -0
  333. package/dist/platform/index.d.ts +4 -0
  334. package/dist/platform/index.js +9 -0
  335. package/dist/platform/registry.d.ts +21 -0
  336. package/dist/platform/registry.js +42 -0
  337. package/dist/platform/toolchain.d.ts +4 -0
  338. package/dist/platform/toolchain.js +43 -0
  339. package/dist/preview/client.d.ts +1 -0
  340. package/dist/preview/client.js +137 -0
  341. package/dist/preview/server.d.ts +5 -0
  342. package/dist/preview/server.js +199 -0
  343. package/dist/profiler/index.d.ts +28 -0
  344. package/dist/profiler/index.js +28 -0
  345. package/dist/profiler/profiler.d.ts +95 -0
  346. package/dist/profiler/profiler.js +338 -0
  347. package/dist/profiler/types.d.ts +87 -0
  348. package/dist/profiler/types.js +12 -0
  349. package/dist/stores/display-profile-store.d.ts +16 -0
  350. package/dist/stores/display-profile-store.js +48 -0
  351. package/dist/stores/theme-store.d.ts +13 -0
  352. package/dist/stores/theme-store.js +30 -0
  353. package/dist/testing.d.ts +45 -0
  354. package/dist/testing.js +47 -0
  355. package/dist/transpile/resolution.d.ts +29 -0
  356. package/dist/transpile/resolution.js +282 -0
  357. package/dist/transpile.d.ts +3 -0
  358. package/dist/transpile.js +722 -0
  359. package/dist/types.d.ts +217 -0
  360. package/dist/types.js +1 -0
  361. package/dist/ui/ui-bridge.d.ts +4 -0
  362. package/dist/ui/ui-bridge.js +34 -0
  363. package/dist/ui-hook.d.ts +84 -0
  364. package/dist/ui-hook.js +45 -0
  365. package/dist/utils/cli.d.ts +3 -0
  366. package/dist/utils/cli.js +463 -0
  367. package/dist/utils/fs.d.ts +4 -0
  368. package/dist/utils/fs.js +23 -0
  369. package/dist/utils/logger.d.ts +2 -0
  370. package/dist/utils/logger.js +8 -0
  371. package/dist/utils/strings.d.ts +35 -0
  372. package/dist/utils/strings.js +142 -0
  373. package/dist/utils/ui.d.ts +66 -0
  374. package/dist/utils/ui.js +149 -0
  375. package/dist/watch.d.ts +35 -0
  376. package/dist/watch.js +111 -0
  377. package/package.json +127 -0
@@ -0,0 +1,564 @@
1
+ // ---------------------------------------------------------------------------
2
+ // UI runtime emitter — injects the C++ runtime header + static tables into the
3
+ // entry translation unit when a UI is mounted.
4
+ //
5
+ // Called from emitCpp between the preamble (includes) and type declarations,
6
+ // gated on ctx.isEntryFile && entryHasUI(). Everything emitted here is
7
+ // file-scope (structs, static tables, extern decls) so it must precede any
8
+ // function that references it.
9
+ //
10
+ // Sources:
11
+ // - emitRuntimeHeader() → the guarded struct/helper block (once per TU)
12
+ // - allLoweredUIModules() → __ui_nodes[] + __ui_trans[] per mounted tree
13
+ // - uiSignalDecls() → one device variable per recorded signal
14
+ // - emitBindingTable(uiBindings()) → __ui_bindings[]
15
+ // ---------------------------------------------------------------------------
16
+ import { appendSourceLine, appendRenderedStatement } from "./line-appender.js";
17
+ import { createChildEmissionScope, createEmissionScopeState } from "../snprintf-helpers.js";
18
+ import { entryHasUI, requireUIHook } from "../../ui-hook.js";
19
+ import { uiSignalDecls, uiBindings, uiPressBindings, watchPinSpecs, clickHandlers } from "../../ir/transformers/ui-call-resolver.js";
20
+ import { emitBindingTable, emitListBindingTable, getListBindings, emitInputBindingTable, getInputBindings } from "../../ir/transformers/ui-reactive.js";
21
+ import { emitCanvasBindings, canvasBindings } from "../../ir/transformers/canvas-lowering.js";
22
+ import { getDisplayProfile } from "../../stores/display-profile-store.js";
23
+ import { effectiveDisplaySize, generateTouchAdapter, normalizeDisplayRotation, resolveScrollConfig, } from "../../api/shared/display-profile.js";
24
+ import { deriveCapabilities } from "../../api/shared/display-capabilities.js";
25
+ import { generateDisplayAdapter } from "../../api/shared/display-adapter.js";
26
+ import { getRadioGroups } from "../../ir/ui-element-auto-wire.js";
27
+ // ---------------------------------------------------------------------------
28
+ // Touch poll body generator — extracted from emitUIRuntime for testability.
29
+ //
30
+ // Emits the ui_poll_touch() body that reads raw touch coords, applies the
31
+ // calibration map+rotation, and dispatches to ui_handle_touch/ui_handle_no_touch.
32
+ //
33
+ // Capacitive controllers (FT6336U, sdl) have no real z/pressure, so the
34
+ // minPressure gate is skipped for them. Resistive controllers (XPT2046,
35
+ // STMPE610, Adafruit_TouchScreen) keep the gate.
36
+ // ---------------------------------------------------------------------------
37
+ const CAPACITIVE_TOUCH_LIBS = new Set(["FT6336U", "sdl"]);
38
+ /** Generate the ui_poll_touch() body, or null for the no-touch path. */
39
+ export function generateTouchPollBody(input) {
40
+ const { library, minPressure, calibration, profile } = input;
41
+ if (!library)
42
+ return null;
43
+ const { xMin, xMax, yMin, yMax } = calibration;
44
+ const rotation = normalizeDisplayRotation(profile.rotation);
45
+ const screen = effectiveDisplaySize(profile);
46
+ const hasNativeSize = profile.nativeWidth !== undefined && profile.nativeHeight !== undefined;
47
+ const nativeWidth = profile.nativeWidth ?? profile.width;
48
+ const nativeHeight = profile.nativeHeight ?? profile.height;
49
+ const rawMapX = `map(__rawX, ${xMin}, ${xMax}, 0, ${nativeWidth})`;
50
+ const rawMapY = `map(__rawY, ${yMin}, ${yMax}, 0, ${nativeHeight})`;
51
+ let mapX, mapY;
52
+ let sdlClamp = false;
53
+ if (library === "sdl") {
54
+ // SDL mouse coords are already window/screen pixel coords — no axis swap,
55
+ // no calibration. Pass them through 1:1 and clamp to the LIVE display
56
+ // dimensions (display_width()/display_height(), which return the SDL
57
+ // target's constructed w_/h_). This way clicks track the window size
58
+ // regardless of the touch.calibration config — the calibration is a
59
+ // hardware-controller concept (resistive raw ranges) that doesn't apply to
60
+ // a mouse, and baking it forced users to keep xMax/yMax in sync with
61
+ // width/height or clicks landed in the wrong place after resizing.
62
+ mapX = `__rawX`;
63
+ mapY = `__rawY`;
64
+ sdlClamp = true;
65
+ }
66
+ else if (!hasNativeSize) {
67
+ const isLandscape = rotation === 1 || rotation === 3;
68
+ const invertX = rotation === 1 || rotation === 2;
69
+ const invertY = rotation === 1 || rotation === 2;
70
+ if (isLandscape) {
71
+ mapX = invertX
72
+ ? `map(__rawX, ${xMin}, ${xMax}, ${profile.width}, 0)`
73
+ : `map(__rawX, ${xMin}, ${xMax}, 0, ${profile.width})`;
74
+ mapY = invertY
75
+ ? `map(__rawY, ${yMin}, ${yMax}, ${profile.height}, 0)`
76
+ : `map(__rawY, ${yMin}, ${yMax}, 0, ${profile.height})`;
77
+ }
78
+ else {
79
+ mapX = `map(__rawY, ${yMin}, ${yMax}, ${profile.width}, 0)`;
80
+ mapY = `map(__rawX, ${xMin}, ${xMax}, 0, ${profile.height})`;
81
+ }
82
+ }
83
+ else {
84
+ switch (rotation) {
85
+ case 1:
86
+ mapX = rawMapY;
87
+ mapY = `${nativeWidth} - (${rawMapX})`;
88
+ break;
89
+ case 2:
90
+ mapX = `${nativeWidth} - (${rawMapX})`;
91
+ mapY = `${nativeHeight} - (${rawMapY})`;
92
+ break;
93
+ case 3:
94
+ mapX = `${nativeHeight} - (${rawMapY})`;
95
+ mapY = rawMapX;
96
+ break;
97
+ default:
98
+ mapX = rawMapX;
99
+ mapY = rawMapY;
100
+ break;
101
+ }
102
+ }
103
+ const clampLines = (indent) => sdlClamp
104
+ ? [
105
+ // SDL: clamp to live display dimensions (display_width()/height() return
106
+ // the SDL target's constructed w_/h_), so clicks track the window size.
107
+ `${indent}int16_t __dw = display_width(), __dh = display_height();`,
108
+ `${indent}if (__tx < 0) __tx = 0; else if (__tx >= __dw) __tx = __dw - 1;`,
109
+ `${indent}if (__ty < 0) __ty = 0; else if (__ty >= __dh) __ty = __dh - 1;`,
110
+ ]
111
+ : [
112
+ `${indent}if (__tx < 0) __tx = 0; else if (__tx >= ${screen.width}) __tx = ${screen.width - 1};`,
113
+ `${indent}if (__ty < 0) __ty = 0; else if (__ty >= ${screen.height}) __ty = ${screen.height - 1};`,
114
+ ];
115
+ const isCapacitive = CAPACITIVE_TOUCH_LIBS.has(library);
116
+ const lines = [
117
+ `void ui_poll_touch() {`,
118
+ ` if (touch_isTouched()) {`,
119
+ ` int16_t __rawX = 0, __rawY = 0, __rawZ = 0;`,
120
+ ` touch_readRaw(&__rawX, &__rawY, &__rawZ);`,
121
+ ];
122
+ if (isCapacitive) {
123
+ lines.push(` int16_t __tx = ${mapX};`, ` int16_t __ty = ${mapY};`, ...clampLines(" "), ` ui_handle_touch(__tx, __ty);`);
124
+ }
125
+ else {
126
+ lines.push(` if (__rawZ >= ${minPressure}) {`, ` int16_t __tx = ${mapX};`, ` int16_t __ty = ${mapY};`, ...clampLines(" "), ` ui_handle_touch(__tx, __ty);`, ` } else {`, ` ui_handle_no_touch();`, ` }`);
127
+ }
128
+ lines.push(` } else {`, ` ui_handle_no_touch();`, ` }`, `}`);
129
+ return lines.join("\n");
130
+ }
131
+ export function emitUIRuntime(ctx) {
132
+ // Only the entry file carries the UI runtime + tables.
133
+ if (!ctx.isEntryFile || !entryHasUI())
134
+ return;
135
+ // The hook is guaranteed set when entryHasUI() returns true.
136
+ const ui = requireUIHook();
137
+ const emitRuntimeHeader = ui.emitRuntimeHeader;
138
+ const allLoweredUIModules = ui.allLoweredUIModules;
139
+ // 0.5. Display adapter: includes + object declaration + inline functions.
140
+ // Generated per display driver type (ILI9341, ST7789, etc.) via the
141
+ // display-adapter registry. Must precede the runtime header so the
142
+ // adapter functions (__tc_display, display_init, etc.) are available.
143
+ const profile = getDisplayProfile();
144
+ // 0. SPI-bus display drivers (ILI9341, ST7796, …) need the SPI library.
145
+ // Host render targets (sdl, native-preview) have no SPI bus, so skip it.
146
+ const spiDriver = profile?.driver !== "sdl" && profile?.driver !== "native-preview";
147
+ if (spiDriver && !ctx.includes.includes("<SPI.h>")) {
148
+ ctx.includes.push("<SPI.h>");
149
+ }
150
+ // NOTE: <stdio.h> (needed by text-binding snprintf bodies) is pushed in
151
+ // buildEmitterContext (setup.ts), NOT here — emitPreamble runs before this
152
+ // function, so a push here would be too late to land in the output.
153
+ if (profile) {
154
+ const adapter = generateDisplayAdapter(profile);
155
+ // Prepend includes to the very front — Arduino's auto-prototyper scans
156
+ // the whole .ino and generates prototypes that reference GFXcanvas16
157
+ // etc. before any #include, so the GFX header must come first.
158
+ ctx.sourceLines.unshift(adapter.includes);
159
+ // Color-depth preamble: UI_COLOR_T is referenced by adapter draw
160
+ // signatures, so the depth + type defines must precede the adapter
161
+ // functions (which come before the runtime header). Under 565/mono
162
+ // these resolve to uint16_t/0x7BEF — byte-identical with history.
163
+ const is888 = profile.colorFormat === "rgb666" || profile.colorFormat === "rgb888";
164
+ ctx.sourceLines.push(is888
165
+ ? "#define UI_COLOR_DEPTH 888\n#define UI_COLOR_T uint32_t\n#define UI_DIM_MASK 0x7F7F7Fu"
166
+ : "#define UI_COLOR_DEPTH 565\n#define UI_COLOR_T uint16_t\n#define UI_DIM_MASK 0x7BEFu");
167
+ ctx.sourceLines.push(adapter.declaration);
168
+ ctx.sourceLines.push(adapter.functions);
169
+ }
170
+ // 0.6. Touch adapter: includes + declaration (zero-cost, transpile-time).
171
+ // Same pattern as the display adapter — generated inline C++ per library type.
172
+ let touchAdapter = null;
173
+ if (profile.touch) {
174
+ const t = profile.touch;
175
+ if (t.library) {
176
+ touchAdapter = generateTouchAdapter(t);
177
+ // Prepend includes to the front (Arduino auto-prototyper safety).
178
+ ctx.sourceLines.unshift(touchAdapter.includes[0]);
179
+ for (let i = 1; i < touchAdapter.includes.length; i++) {
180
+ ctx.sourceLines.push(touchAdapter.includes[i]);
181
+ }
182
+ ctx.sourceLines.push(touchAdapter.declaration);
183
+ ctx.sourceLines.push(touchAdapter.functions);
184
+ }
185
+ }
186
+ // SDL_MAIN_HANDLED must be the absolute first line — it must precede every
187
+ // #include <SDL2/SDL.h> (the display adapter's and the touch shim's). The
188
+ // touch unshift above runs after the display-adapter block, so this final
189
+ // unshift lands at the very front.
190
+ if (profile.driver === "sdl") {
191
+ ctx.sourceLines.unshift("#define SDL_MAIN_HANDLED");
192
+ }
193
+ // Forward declaration for touch poll (used inside the runtime header's ui_tick).
194
+ // Always emitted — ui_tick calls it unconditionally; the body is a no-op when
195
+ // no touch hardware is configured.
196
+ ctx.sourceLines.push("void ui_poll_touch();");
197
+ // 1. Runtime header (structs + helpers, guarded so repeat emission is safe).
198
+ // Emit the antialiasing compile-time flag before the header so the AA
199
+ // code paths are compiled in.
200
+ const loweredModules = allLoweredUIModules();
201
+ // AA compiles in only when the profile opts in. A node-level fontAntialias=1
202
+ // flag alone is not enough — that would silently re-enable a heavy code path
203
+ // the author explicitly disabled with `antialias: false`. (The runtime still
204
+ // honors fontAntialias=0 at the per-node level when AA is compiled in.)
205
+ const needsAntialias = profile.antialias === true;
206
+ if (needsAntialias) {
207
+ ctx.sourceLines.push("#define UI_AA 1");
208
+ }
209
+ // The SDL desktop target has a real keyboard (the host event loop routes
210
+ // SDL_TEXTINPUT into the on-screen-keyboard editing session), so the 6×4 OSK
211
+ // grid is redundant clutter that covers the app. UI_HIDE_OSK suppresses the
212
+ // OSK *draw* only — the editing session (buffer, target, commit-on-close)
213
+ // still runs, so typing works. Hardware targets don't define it: their only
214
+ // text-entry path is the visible grid.
215
+ if (profile.driver === "sdl") {
216
+ ctx.sourceLines.push("#define UI_HIDE_OSK 1");
217
+ }
218
+ // 1a. Color depth — drives blend/lerp selection in the runtime header. 565
219
+ // for TFT byte-identity (node fields hold 565 values); 888 for rgb666+
220
+ // targets (node fields hold 888 values, blend via ui_blend888). The value
221
+ // depth and blend math switch TOGETHER — see Phase 1 counterexample.
222
+ // Guarded: the display-adapter preamble (0.5) may already define it so
223
+ // UI_COLOR_T precedes the adapter's draw signatures.
224
+ ctx.sourceLines.push(profile.colorFormat === "rgb666" || profile.colorFormat === "rgb888"
225
+ ? "#ifndef UI_COLOR_DEPTH\n#define UI_COLOR_DEPTH 888\n#endif"
226
+ : "#ifndef UI_COLOR_DEPTH\n#define UI_COLOR_DEPTH 565\n#endif");
227
+ // 1a-bis. Refresh model + native format from derived capabilities. E-ink
228
+ // (displayClass: "eink") derives refreshModel "deferred-partial" +
229
+ // requiresBackingStore; TFT stays "immediate". When these are absent
230
+ // (TFT), the runtime's backing-store + refresh-scheduler + clear-
231
+ // suppression paths compile out — byte-identical with pre-Phase-4.
232
+ const caps = deriveCapabilities(profile);
233
+ if (caps.refreshModel !== "immediate") {
234
+ ctx.sourceLines.push("#define UI_REFRESH_DEFERRED 1");
235
+ }
236
+ if (caps.nativeFormat === "mono") {
237
+ ctx.sourceLines.push("#define UI_NATIVE_MONO 1");
238
+ }
239
+ if (caps.requiresBackingStore) {
240
+ ctx.sourceLines.push("#define UI_REQUIRES_BACKING_STORE 1");
241
+ }
242
+ // NOTE: per-frame SPI-write batching via UI_BATCH_SPI_WRITES was investigated
243
+ // and reverted — the Adafruit_GFX version in this repo does NOT reference-
244
+ // count startWrite/endWrite (every endWrite raises CS unconditionally), so
245
+ // wrapping the frame in startWrite/endWrite causes inner draw calls (e.g. the
246
+ // scroll-canvas composite) to close the transaction mid-frame → black screen.
247
+ // The three-branch dispatch in runtime-header.ts still ships (it's a clearer
248
+ // structure than the old #ifndef/#else), but TFT now falls through to the
249
+ // no-op default until a ref-counted library version or a bypass approach lands.
250
+ // See docs/superpowers/specs/2026-07-06-tft-spi-write-batching-design.md.
251
+ // 1b. Scroll capability + physics overrides — emitted BEFORE the runtime
252
+ // header so its #ifndef guards adopt them. Source of truth:
253
+ // resolveScrollConfig(profile.scroll). Defaults derive from the declared
254
+ // touch hardware, so the demo (XPT2046) gets resistive+full with no config.
255
+ const scroll = resolveScrollConfig(profile, { buildTarget: profile._buildTarget });
256
+ ctx.sourceLines.push(`#define UI_SCROLL_MAX_OVERSCROLL ${scroll.maxOverscroll}`, `#define UI_SCROLL_STIFFNESS_X10 ${Math.round(scroll.stiffness * 10)}`, `#define UI_SCROLL_EDGE_SNAP_PX ${scroll.edgeSnapPx}`, `#define UI_SCROLL_DRAG_SCALE_X10 ${Math.round(scroll.dragScale * 10)}`, `#define UI_SCROLL_INPUT_TIER_CAPACITIVE ${scroll.inputTier === "capacitive" ? 1 : 0}`, `#define UI_SCROLL_INPUT_TIER_RESISTIVE ${scroll.inputTier === "resistive" ? 1 : 0}`, `#define UI_SCROLL_INPUT_TIER_NONE ${scroll.inputTier === "none" ? 1 : 0}`, `#define UI_SCROLL_RENDER_TIER_FULL ${scroll.renderTier === "full" ? 1 : 0}`, `#define UI_SCROLL_RENDER_TIER_CONSTRAINED ${scroll.renderTier === "constrained" ? 1 : 0}`, `#define UI_SCROLL_CANVAS_BUDGET_BYTES ${scroll.scrollCanvasBudgetBytes}`, ...(scroll.debug ? [`#define UI_SCROLL_DEBUG 1`] : []));
257
+ // Forward-declare __ui_kb_set_onchange before the runtime header: the
258
+ // header's keyboard-open function calls it, but the definition is emitted
259
+ // later (section 8b). On native (single TU) this must precede the header.
260
+ ctx.sourceLines.push(`void __ui_kb_set_onchange();`);
261
+ ctx.sourceLines.push(emitRuntimeHeader());
262
+ // 1.5. Touch poll function (uses the adapter pattern). Body is generated by
263
+ // generateTouchPollBody — extracted for unit-testability of the capacitive
264
+ // vs resistive branch (FT6336U/SDL skip the minPressure gate).
265
+ if (profile.touch && touchAdapter) {
266
+ const body = generateTouchPollBody({
267
+ library: profile.touch.library,
268
+ minPressure: profile.touch.minPressure ?? 10,
269
+ calibration: profile.touch.calibration,
270
+ profile: {
271
+ width: profile.width,
272
+ height: profile.height,
273
+ nativeWidth: profile.nativeWidth,
274
+ nativeHeight: profile.nativeHeight,
275
+ rotation: profile.rotation,
276
+ },
277
+ });
278
+ ctx.sourceLines.push(body ?? "void ui_poll_touch() {}");
279
+ }
280
+ else {
281
+ // No touch hardware — emit a no-op so ui_tick's unconditional call resolves.
282
+ ctx.sourceLines.push("void ui_poll_touch() {}");
283
+ }
284
+ // 2. Static node + transition tables for every mounted UI tree.
285
+ for (const { lowered } of allLoweredUIModules()) {
286
+ ctx.sourceLines.push(lowered.fontTables);
287
+ if (lowered.imageTables)
288
+ ctx.sourceLines.push(lowered.imageTables);
289
+ if (lowered.keyframeTables)
290
+ ctx.sourceLines.push(lowered.keyframeTables);
291
+ ctx.sourceLines.push(lowered.nodeTable);
292
+ ctx.sourceLines.push(lowered.transitionTable);
293
+ }
294
+ // 2b. Keyboard loader functions + dispatch table (for <input> support).
295
+ for (const { lowered } of allLoweredUIModules()) {
296
+ if (lowered.keyboardLoaders)
297
+ ctx.sourceLines.push(lowered.keyboardLoaders);
298
+ if (lowered.keyboardDispatch)
299
+ ctx.sourceLines.push(lowered.keyboardDispatch);
300
+ }
301
+ // 3. Signal variables (one per ui.signal / const X = ui.signal).
302
+ for (const decl of uiSignalDecls()) {
303
+ ctx.sourceLines.push(decl);
304
+ }
305
+ // 4. Binding table (accumulated from ui.bind calls).
306
+ // Forward-declare the binding compute functions first: the table references
307
+ // them via fn/textFn, but the function bodies are emitted below (4b). On
308
+ // native (single TU, no Arduino auto-prototyper) the use-before-def would
309
+ // fail to compile.
310
+ for (const spec of uiBindings()) {
311
+ if (spec.property === "text") {
312
+ ctx.sourceLines.push(`void ${spec.fnName}(char* buf, uint8_t size);`);
313
+ }
314
+ else {
315
+ ctx.sourceLines.push(`uint32_t ${spec.fnName}();`);
316
+ }
317
+ }
318
+ ctx.sourceLines.push(emitBindingTable(uiBindings()));
319
+ // 4a. List binding functions + table (from ui.bindList calls).
320
+ for (const spec of getListBindings()) {
321
+ emitCallbackWrapper(ctx, spec.countFnName, spec.countFnBody, spec.countStatements, spec.countSourceSpan, "()", "uint16_t");
322
+ emitCallbackWrapper(ctx, spec.itemFnName, spec.itemFnBody, spec.itemStatements, spec.itemSourceSpan, "(uint16_t idx, char* buf, uint8_t size)", "void");
323
+ if (spec.tapFnName && (spec.tapStatements || spec.tapFnBody)) {
324
+ emitCallbackWrapper(ctx, spec.tapFnName, spec.tapFnBody ?? "", spec.tapStatements, spec.tapSourceSpan, "(uint16_t idx)", "void");
325
+ }
326
+ }
327
+ ctx.sourceLines.push(emitListBindingTable(getListBindings()));
328
+ // 4a'. Input binding functions + table (from ui.bindInput calls).
329
+ for (const spec of getInputBindings()) {
330
+ emitCallbackWrapper(ctx, spec.cbFnName, spec.cbFnBody, spec.bodyStatements, spec.sourceSpan, "(const char* text)", "void");
331
+ }
332
+ ctx.sourceLines.push(emitInputBindingTable(getInputBindings()));
333
+ // 4b. Binding compute functions. Each ui.bind(node, prop, fn) records a
334
+ // BindingSpec whose fnName is referenced by the table. v1 emits a stub that
335
+ // returns the node's current property value — structurally valid so the
336
+ // table compiles. Full arrow-function → C++ lowering is a follow-up.
337
+ for (const spec of uiBindings()) {
338
+ // If the binding has a lowered C++ expression (from the arrow body), use it;
339
+ // otherwise fall back to returning the node's current value.
340
+ const isTextBinding = spec.property === "text";
341
+ if (isTextBinding) {
342
+ // Text bindings are void fill-style: they write into (buf, size).
343
+ // cppBody is the imperative snprintf statement from lowerTextBindingBody.
344
+ const body = spec.cppBody ?? "buf[0] = 0;";
345
+ ctx.sourceLines.push(`void ${spec.fnName}(char* buf, uint8_t size) { ${body} }`);
346
+ }
347
+ else {
348
+ const access = spec.property === "background" ? "bg" : spec.property === "color" ? "fg" : "bg";
349
+ // Prefer the stashed ExpressionIR: render it through the strategy-aware
350
+ // ExpressionRenderer so division promotion, modulo→fmod, and concat
351
+ // wrapping match top-level code. Fall back to the prerendered cppExpr
352
+ // (color-resolved via resolveColorLiterals) when no IR is available.
353
+ if (spec.bodyIR) {
354
+ const rendered = ctx.exprRenderer.render(spec.bodyIR);
355
+ const prelude = ctx.exprRenderer.drainPrelude();
356
+ // Prelude lines (e.g. __cuttlefish_str_N buffer decls for snprintf
357
+ // expansions) must land inside the function body before the return;
358
+ // promote char→static char so a returned buffer pointer doesn't dangle
359
+ // (mirrors statement-renderer's return+prelude handling).
360
+ const body = prelude.length > 0
361
+ ? `${prelude.map((l) => l.replace(/\bchar\s+/, "static char ")).join(" ")} return ${rendered};`
362
+ : `return ${rendered};`;
363
+ ctx.sourceLines.push(`uint32_t ${spec.fnName}(void) { ${body} }`);
364
+ }
365
+ else {
366
+ const body = spec.cppExpr || `__ui_nodes[${spec.nodeIndex}].${access}`;
367
+ ctx.sourceLines.push(`uint32_t ${spec.fnName}(void) { return ${body}; }`);
368
+ }
369
+ }
370
+ }
371
+ // 5. Node count externs the runtime header references.
372
+ const totalNodes = allLoweredUIModules().reduce((sum, { lowered }) => sum + countNodes(lowered.nodeTable), 0);
373
+ ctx.sourceLines.push(`const uint16_t __ui_node_count = ${totalNodes};`);
374
+ ctx.sourceLines.push(`const uint16_t __ui_trans_count = ${countTransitions()};`);
375
+ ctx.sourceLines.push(`const uint16_t __ui_binding_count = ${uiBindings().length};`);
376
+ // Count screens across all modules (for multi-screen navigation).
377
+ let maxScreens = 1;
378
+ for (const { lowered } of allLoweredUIModules()) {
379
+ if (lowered.screenCount > maxScreens)
380
+ maxScreens = lowered.screenCount;
381
+ }
382
+ ctx.sourceLines.push(`const uint16_t __ui_screen_count = ${maxScreens};`);
383
+ // 6. Press/release handler functions (from screen.btn.onPress/onRelease).
384
+ // Each calls ui_on_press/ui_on_release(nodeIndex), defined in the runtime
385
+ // header. The attachInterrupt calls that wire these to pins are injected
386
+ // into setup() by the entrypoint synthesizer.
387
+ for (const pb of uiPressBindings()) {
388
+ const fn = pb.edge === "press" ? "ui_on_press" : "ui_on_release";
389
+ ctx.sourceLines.push(`void ${pb.handlerName}() { ${fn}(${pb.nodeIndex}); }`);
390
+ }
391
+ // 7. Pin-watcher callbacks + table (from ui.watchPin(pin, callback)).
392
+ // Each callback is a plain function; the runtime's ui_poll_inputs() calls it
393
+ // on falling edge. No async runtime needed — runs in the main loop frame.
394
+ for (const wp of watchPinSpecs()) {
395
+ emitCallbackWrapper(ctx, wp.fnName, wp.callbackBody, wp.bodyStatements, wp.sourceSpan);
396
+ }
397
+ if (watchPinSpecs().length > 0) {
398
+ ctx.sourceLines.push(`UIPinWatch __ui_pin_watches[] = {`);
399
+ for (const wp of watchPinSpecs()) {
400
+ ctx.sourceLines.push(` { .pin=${wp.pin}, .lastState=1, .cb=${wp.fnName} },`);
401
+ }
402
+ ctx.sourceLines.push(`};`);
403
+ ctx.sourceLines.push(`const uint16_t __ui_pin_watch_count = ${watchPinSpecs().length};`);
404
+ }
405
+ else {
406
+ ctx.sourceLines.push(`UIPinWatch __ui_pin_watches[] = {};`);
407
+ ctx.sourceLines.push(`const uint16_t __ui_pin_watch_count = 0;`);
408
+ }
409
+ // 8. Touch: handler functions + tables (click, hold, release).
410
+ // "change" (input commit) and "rangechange" (slider drag) are emitted
411
+ // separately in 8b/8c — they have their own dispatch points.
412
+ const touchHandlers = clickHandlers().filter(h => h.kind !== "change" && h.kind !== "rangechange");
413
+ const maxIdx = touchHandlers.reduce((max, h) => Math.max(max, h.nodeIndex), -1);
414
+ const tableSize = Math.max(maxIdx + 1, 1);
415
+ // Forward-declare named-ref handlers (on:* attributes) BEFORE the handler
416
+ // tables — the table references these author-declared functions by name, but
417
+ // their definitions are emitted later (in the author-function section). The
418
+ // general transpiler's own forward declarations land after the UI tables, so
419
+ // without these the table's reference is out of scope.
420
+ for (const ch of touchHandlers) {
421
+ if (ch.isNamedRef) {
422
+ ctx.sourceLines.push(`void ${ch.fnName}();`);
423
+ }
424
+ }
425
+ // Handler functions (skip named-ref handlers — their function is the author's
426
+ // own exported function, already emitted by the general transpiler pipeline).
427
+ for (const ch of touchHandlers) {
428
+ if (ch.isNamedRef)
429
+ continue;
430
+ emitCallbackWrapper(ctx, ch.fnName, ch.callbackBody, ch.bodyStatements, ch.sourceSpan);
431
+ }
432
+ // Build lookup tables (null for nodes without handlers)
433
+ const buildTable = (kind) => {
434
+ const entries = [];
435
+ for (let i = 0; i < tableSize; i++) {
436
+ const handler = clickHandlers().find(h => h.nodeIndex === i && h.kind === kind);
437
+ entries.push(handler ? handler.fnName : "nullptr");
438
+ }
439
+ return entries.join(", ");
440
+ };
441
+ if (profile.touch) {
442
+ ctx.sourceLines.push(`void (*__ui_click_handlers[])() = { ${buildTable("click")} };`);
443
+ ctx.sourceLines.push(`void (*__ui_hold_handlers[])() = { ${buildTable("hold")} };`);
444
+ ctx.sourceLines.push(`void (*__ui_release_handlers[])() = { ${buildTable("release")} };`);
445
+ ctx.sourceLines.push(`const uint16_t __ui_click_handler_count = ${tableSize};`);
446
+ }
447
+ else {
448
+ ctx.sourceLines.push(`void (*__ui_click_handlers[])() = {};`);
449
+ ctx.sourceLines.push(`void (*__ui_hold_handlers[])() = {};`);
450
+ ctx.sourceLines.push(`void (*__ui_release_handlers[])() = {};`);
451
+ ctx.sourceLines.push(`const uint16_t __ui_click_handler_count = 0;`);
452
+ }
453
+ // 8b. Input onChange dispatch — assigns __ui_kb_onchange based on __ui_kb_target.
454
+ // Forward-declare __ui_kb_set_onchange unconditionally: the runtime header's
455
+ // keyboard-open function (emitted earlier) calls it, and on native (single
456
+ // TU) the definition below would be too late.
457
+ ctx.sourceLines.push(`void __ui_kb_set_onchange();`);
458
+ const inputChangeHandlers = clickHandlers().filter(h => h.kind === "change");
459
+ if (inputChangeHandlers.length > 0) {
460
+ // Forward-declare named-ref onChange handlers before the dispatch wiring.
461
+ for (const h of inputChangeHandlers) {
462
+ if (h.isNamedRef)
463
+ ctx.sourceLines.push(`void ${h.fnName}();`);
464
+ }
465
+ for (const h of inputChangeHandlers) {
466
+ if (h.isNamedRef)
467
+ continue; // author's own function; no wrapper.
468
+ emitCallbackWrapper(ctx, h.fnName, h.callbackBody, h.bodyStatements, h.sourceSpan);
469
+ }
470
+ ctx.sourceLines.push(`void __ui_kb_set_onchange() {`);
471
+ ctx.sourceLines.push(` __ui_kb_onchange = nullptr;`);
472
+ for (const h of inputChangeHandlers) {
473
+ ctx.sourceLines.push(` if (__ui_kb_target == ${h.nodeIndex}) __ui_kb_onchange = ${h.fnName};`);
474
+ }
475
+ ctx.sourceLines.push(`}`);
476
+ }
477
+ else {
478
+ ctx.sourceLines.push(`void __ui_kb_set_onchange() { __ui_kb_onchange = nullptr; }`);
479
+ }
480
+ // 8c. Range onChange dispatch — per-node handler table fired from the slider
481
+ // drag loop on every value change. Mirrors the click/hold/release tables.
482
+ const rangeChangeHandlers = clickHandlers().filter(h => h.kind === "rangechange");
483
+ const rcMaxIdx = rangeChangeHandlers.reduce((max, h) => Math.max(max, h.nodeIndex), -1);
484
+ const rcTableSize = Math.max(rcMaxIdx + 1, 1);
485
+ for (const h of rangeChangeHandlers) {
486
+ emitCallbackWrapper(ctx, h.fnName, h.callbackBody, h.bodyStatements, h.sourceSpan);
487
+ }
488
+ if (profile.touch && rangeChangeHandlers.length > 0) {
489
+ const rcEntries = [];
490
+ for (let i = 0; i < rcTableSize; i++) {
491
+ const handler = rangeChangeHandlers.find(h => h.nodeIndex === i);
492
+ rcEntries.push(handler ? handler.fnName : "nullptr");
493
+ }
494
+ ctx.sourceLines.push(`void (*__ui_rangechange_handlers[])() = { ${rcEntries.join(", ")} };`);
495
+ ctx.sourceLines.push(`const uint16_t __ui_rangechange_handler_count = ${rcTableSize};`);
496
+ }
497
+ else {
498
+ ctx.sourceLines.push(`void (*__ui_rangechange_handlers[])() = {};`);
499
+ ctx.sourceLines.push(`const uint16_t __ui_rangechange_handler_count = 0;`);
500
+ }
501
+ // 9. Radio group table (from auto-wire).
502
+ const radioGroups = getRadioGroups();
503
+ if (radioGroups.size > 0) {
504
+ const groupEntries = Array.from(radioGroups.entries());
505
+ ctx.sourceLines.push(`UIRadioGroup __ui_radio_groups[] = {`);
506
+ for (const [, members] of groupEntries) {
507
+ const indices = members.map((m) => m.nodeIndex).join(", ");
508
+ ctx.sourceLines.push(` { .nodeIndices={${indices}}, .count=${members.length} },`);
509
+ }
510
+ ctx.sourceLines.push(`};`);
511
+ ctx.sourceLines.push(`const uint16_t __ui_radio_group_count = ${groupEntries.length};`);
512
+ }
513
+ else {
514
+ ctx.sourceLines.push(`UIRadioGroup __ui_radio_groups[] = {};`);
515
+ ctx.sourceLines.push(`const uint16_t __ui_radio_group_count = 0;`);
516
+ }
517
+ // 10. Canvas draw bindings (ui.drawCanvas). Each spec emits a draw wrapper
518
+ // that runs the lowered callback body against the node's offscreen canvas.
519
+ emitCanvasBindings(canvasBindings(), (line, span) => {
520
+ appendSourceLine(ctx, line, span ? { tsSpan: span, nodeKind: "ui-canvas-callback" } : undefined);
521
+ }, (statements, indent) => {
522
+ const scope = createChildEmissionScope(createEmissionScopeState());
523
+ for (const stmt of statements) {
524
+ appendRenderedStatement(ctx, stmt, indent, scope);
525
+ }
526
+ });
527
+ }
528
+ /** Emit a synthesized callback wrapper. When bodyStatements is present, each
529
+ * statement is rendered through StatementRenderer (same path setInterval /
530
+ * free functions use). Falls back to pasting callbackBody for auto-wire /
531
+ * legacy string-baked specs. Skipped by callers for isNamedRef handlers. */
532
+ function emitCallbackWrapper(ctx, fnName, callbackBody, bodyStatements, sourceSpan, signature = "()", returnType = "void") {
533
+ const mapEntry = sourceSpan
534
+ ? { tsSpan: sourceSpan, nodeKind: "ui-callback", symbolName: fnName }
535
+ : undefined;
536
+ if (bodyStatements && bodyStatements.length > 0) {
537
+ appendSourceLine(ctx, `${returnType} ${fnName}${signature} {`, mapEntry);
538
+ const scope = createChildEmissionScope(createEmissionScopeState());
539
+ for (const stmt of bodyStatements) {
540
+ appendRenderedStatement(ctx, stmt, " ", scope);
541
+ }
542
+ appendSourceLine(ctx, `}`);
543
+ return;
544
+ }
545
+ appendSourceLine(ctx, `${returnType} ${fnName}${signature} { ${callbackBody || ""} }`, mapEntry);
546
+ }
547
+ /** Count NODE_FILL/NODE_TEXT entries in the emitted node table (one per node). */
548
+ function countNodes(nodeTable) {
549
+ // Count node-table entries directly. Every node initializer starts with
550
+ // "{ .box="; counting those cannot under/over-count regardless of kind (the
551
+ // previous kind-regex approach missed NODE_CANVAS and any future kind,
552
+ // undercounting __ui_node_count so the last node(s) were never drawn).
553
+ const matches = nodeTable.match(/\{\s*\.box=/g);
554
+ return matches ? matches.length : 0;
555
+ }
556
+ /** Count armed transitions across all modules. */
557
+ function countTransitions() {
558
+ let count = 0;
559
+ for (const { lowered } of requireUIHook().allLoweredUIModules()) {
560
+ const matches = lowered.transitionTable.match(/\.durationMs=/g);
561
+ count += matches ? matches.length : 0;
562
+ }
563
+ return count;
564
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};