@auraindustry/aurajs 0.0.7 → 0.1.0

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 (334) hide show
  1. package/README.md +98 -2
  2. package/benchmarks/perf-thresholds.json +54 -0
  3. package/package.json +4 -7
  4. package/src/asset-pack.mjs +5 -1
  5. package/src/authored-project.mjs +1449 -0
  6. package/src/authored-runtime.mjs +2016 -0
  7. package/src/authoring/avatar-animation-graph.mjs +648 -0
  8. package/src/bin-integrity.mjs +272 -0
  9. package/src/build-contract/assets.mjs +130 -0
  10. package/src/build-contract/capabilities.mjs +116 -0
  11. package/src/build-contract/constants.mjs +6 -0
  12. package/src/build-contract/helpers.mjs +44 -0
  13. package/src/build-contract/web-templates.mjs +5993 -0
  14. package/src/build-contract.mjs +27 -2910
  15. package/src/bundler.mjs +188 -55
  16. package/src/cli.mjs +4825 -1512
  17. package/src/commands/project-authoring.mjs +434 -0
  18. package/src/config.mjs +27 -0
  19. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +3309 -0
  20. package/src/conformance/cases/core-runtime-cases.mjs +1431 -0
  21. package/src/conformance/cases/index.mjs +11 -0
  22. package/src/conformance/cases/scene3d-and-media-cases.mjs +2094 -0
  23. package/src/conformance/cases/systems-and-gameplay-cases.mjs +1776 -0
  24. package/src/conformance/shared.mjs +27 -0
  25. package/src/conformance-runner.mjs +25 -13
  26. package/src/conformance.mjs +619 -4020
  27. package/src/cutscene.mjs +362 -5
  28. package/src/dev-cli-action.mjs +249 -0
  29. package/src/dev-cli-inspect.mjs +92 -0
  30. package/src/dev-cli-state.mjs +80 -0
  31. package/src/external-asset-cache.mjs +587 -0
  32. package/src/external-asset-policy.mjs +217 -0
  33. package/src/external-package-surface.mjs +206 -0
  34. package/src/game-action-runtime.mjs +869 -0
  35. package/src/game-state-runtime.mjs +206 -6
  36. package/src/headless-action.mjs +186 -0
  37. package/src/headless-test/runtime-animation.mjs +1173 -0
  38. package/src/headless-test/runtime-coordinator.mjs +1514 -0
  39. package/src/headless-test/runtime-primitives.mjs +320 -0
  40. package/src/headless-test/runtime-world.mjs +2253 -0
  41. package/src/headless-test.mjs +392 -4298
  42. package/src/host-binary.mjs +342 -14
  43. package/src/icon-discovery.mjs +64 -0
  44. package/src/make-catalog.mjs +109 -0
  45. package/src/make.mjs +197 -0
  46. package/src/package-integrity.mjs +586 -0
  47. package/src/perf-benchmark.mjs +353 -0
  48. package/src/postinstall.mjs +5 -5
  49. package/src/prefabs/index.mjs +34 -0
  50. package/src/prefabs/scene-serialization.mjs +184 -0
  51. package/src/project-importer.mjs +620 -0
  52. package/src/project-registry.mjs +24 -0
  53. package/src/publish-command.mjs +195 -0
  54. package/src/publish-env-example.mjs +83 -0
  55. package/src/publish-validation.mjs +708 -0
  56. package/src/retro/assets/compile.mjs +232 -0
  57. package/src/retro/backend-gba/authoring.mjs +1029 -0
  58. package/src/retro/backend-gba/rom.mjs +363 -0
  59. package/src/retro/backend-gbc/rom.mjs +85 -0
  60. package/src/retro/build.mjs +278 -0
  61. package/src/retro/cli/commands.mjs +292 -0
  62. package/src/retro/cli/templates.mjs +84 -0
  63. package/src/retro/diagnostics/catalog.mjs +110 -0
  64. package/src/retro/diagnostics/emit.mjs +72 -0
  65. package/src/retro/emulator/case-overlay.mjs +64 -0
  66. package/src/retro/emulator/discovery.mjs +158 -0
  67. package/src/retro/emulator/macos-case-overlay.swift +220 -0
  68. package/src/retro/emulator/profiles.mjs +146 -0
  69. package/src/retro/emulator/runner.mjs +289 -0
  70. package/src/retro/frontend/load-project.mjs +98 -0
  71. package/src/retro/index.mjs +30 -0
  72. package/src/retro/ir/build-ir.mjs +108 -0
  73. package/src/retro/runtime-gba/contract.mjs +151 -0
  74. package/src/retro/runtime-gbc/contract.mjs +117 -0
  75. package/src/retro/shared/span.mjs +26 -0
  76. package/src/retro/shared/targets.mjs +64 -0
  77. package/src/retro/validator/check-project.mjs +114 -0
  78. package/src/runtime-hotspot-audit.mjs +707 -0
  79. package/src/scaffold/config.mjs +1000 -0
  80. package/src/scaffold/fs.mjs +56 -0
  81. package/src/scaffold/layout.mjs +318 -0
  82. package/src/scaffold/project-docs.mjs +438 -0
  83. package/src/scaffold.mjs +93 -596
  84. package/src/scene-composition/index.mjs +326 -0
  85. package/src/scene-composition/runtime.mjs +751 -0
  86. package/src/self-hosted-assets.mjs +604 -0
  87. package/src/session-client.mjs +750 -0
  88. package/src/session-native-launcher.mjs +74 -0
  89. package/src/session-protocol.mjs +75 -0
  90. package/src/session-runtime.mjs +321 -0
  91. package/src/session-server.mjs +360 -0
  92. package/src/shader-kits/index.mjs +773 -0
  93. package/src/starter-content-registry.mjs +292 -0
  94. package/src/state-artifacts.mjs +662 -24
  95. package/src/state-dev-reload.mjs +99 -2
  96. package/src/terminal-ui.mjs +245 -0
  97. package/src/web-conformance.mjs +219 -0
  98. package/templates/create/2d/config/gameplay/shooter.config.js +26 -0
  99. package/templates/create/2d/content/gameplay/waves.json +26 -0
  100. package/templates/create/2d/content/registries/.gitkeep +1 -0
  101. package/templates/create/2d/docs/design/.gitkeep +1 -0
  102. package/templates/create/2d/docs/design/loop.md +5 -0
  103. package/templates/create/2d/prefabs/enemies.prefab.js +90 -0
  104. package/templates/create/2d/prefabs/enemy-basic.prefab.js +18 -0
  105. package/templates/create/2d/prefabs/player.prefab.js +36 -0
  106. package/templates/create/2d/prefabs/projectiles.prefab.js +35 -0
  107. package/templates/create/2d/scenes/boot.scene.js +12 -0
  108. package/templates/create/2d/scenes/gameplay.scene.js +230 -0
  109. package/templates/create/2d/scenes/menu.scene.js +9 -0
  110. package/templates/create/2d/src/main.js +6 -185
  111. package/templates/create/2d/src/runtime/app.js +49 -0
  112. package/templates/create/2d/src/runtime/capabilities.js +35 -0
  113. package/templates/create/2d/ui/hud.screen.js +40 -0
  114. package/templates/create/2d/ui/pause.screen.js +149 -0
  115. package/templates/create/2d/ui/settings.screen.js +347 -0
  116. package/templates/create/2d/ui/title.screen.js +13 -0
  117. package/templates/create/2d-adventure/aura.config.json +28 -0
  118. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +14 -0
  119. package/templates/create/2d-adventure/content/gameplay/world.js +46 -0
  120. package/templates/create/2d-adventure/content/registries/.gitkeep +1 -0
  121. package/templates/create/2d-adventure/docs/design/loop.md +5 -0
  122. package/templates/create/2d-adventure/prefabs/player.prefab.js +54 -0
  123. package/templates/create/2d-adventure/prefabs/relic.prefab.js +38 -0
  124. package/templates/create/2d-adventure/prefabs/world.prefab.js +125 -0
  125. package/templates/create/2d-adventure/scenes/gameplay.scene.js +256 -0
  126. package/templates/create/2d-adventure/src/runtime/capabilities.js +34 -0
  127. package/templates/create/2d-adventure/ui/hud.screen.js +60 -0
  128. package/templates/create/2d-survivor/config/gameplay/survivor.config.js +33 -0
  129. package/templates/create/2d-survivor/content/gameplay/spawn-zones.json +29 -0
  130. package/templates/create/2d-survivor/content/registries/.gitkeep +1 -0
  131. package/templates/create/2d-survivor/docs/design/.gitkeep +1 -0
  132. package/templates/create/2d-survivor/docs/design/loop.md +5 -0
  133. package/templates/create/2d-survivor/prefabs/enemies.prefab.js +178 -0
  134. package/templates/create/2d-survivor/prefabs/enemy-swarm.prefab.js +18 -0
  135. package/templates/create/2d-survivor/prefabs/player.prefab.js +42 -0
  136. package/templates/create/2d-survivor/prefabs/projectiles.prefab.js +56 -0
  137. package/templates/create/2d-survivor/scenes/boot.scene.js +12 -0
  138. package/templates/create/2d-survivor/scenes/gameplay.scene.js +314 -0
  139. package/templates/create/2d-survivor/scenes/menu.scene.js +9 -0
  140. package/templates/create/2d-survivor/src/main.js +5 -332
  141. package/templates/create/2d-survivor/src/runtime/app.js +49 -0
  142. package/templates/create/2d-survivor/src/runtime/capabilities.js +35 -0
  143. package/templates/create/2d-survivor/ui/hud.screen.js +45 -0
  144. package/templates/create/2d-survivor/ui/title.screen.js +13 -0
  145. package/templates/create/3d/assets/models/starter-avatar.gltf +184 -0
  146. package/templates/create/3d/config/gameplay/.gitkeep +1 -0
  147. package/templates/create/3d/content/gameplay/checkpoints.json +33 -0
  148. package/templates/create/3d/content/gameplay/course.js +40 -0
  149. package/templates/create/3d/content/registries/.gitkeep +1 -0
  150. package/templates/create/3d/docs/design/.gitkeep +1 -0
  151. package/templates/create/3d/docs/design/loop.md +5 -0
  152. package/templates/create/3d/prefabs/checkpoint.prefab.js +15 -0
  153. package/templates/create/3d/prefabs/player.prefab.js +204 -0
  154. package/templates/create/3d/prefabs/world.prefab.js +112 -0
  155. package/templates/create/3d/scenes/boot.scene.js +12 -0
  156. package/templates/create/3d/scenes/checkpoint.scene.js +9 -0
  157. package/templates/create/3d/scenes/gameplay.scene.js +292 -0
  158. package/templates/create/3d/src/main.js +6 -295
  159. package/templates/create/3d/src/runtime/app.js +49 -0
  160. package/templates/create/3d/src/runtime/capabilities.js +53 -0
  161. package/templates/create/3d/src/runtime/materials.js +34 -0
  162. package/templates/create/3d/src/runtime/state.js +39 -0
  163. package/templates/create/3d/ui/hud.screen.js +75 -0
  164. package/templates/create/3d/ui/pause.screen.js +166 -0
  165. package/templates/create/3d/ui/settings.screen.js +387 -0
  166. package/templates/create/3d-adventure/assets/models/starter-avatar.gltf +184 -0
  167. package/templates/create/3d-adventure/aura.config.json +28 -0
  168. package/templates/create/3d-adventure/config/gameplay/adventure.config.js +9 -0
  169. package/templates/create/3d-adventure/content/gameplay/course.js +62 -0
  170. package/templates/create/3d-adventure/content/registries/.gitkeep +1 -0
  171. package/templates/create/3d-adventure/docs/design/loop.md +5 -0
  172. package/templates/create/3d-adventure/prefabs/player.prefab.js +168 -0
  173. package/templates/create/3d-adventure/prefabs/relic.prefab.js +35 -0
  174. package/templates/create/3d-adventure/prefabs/world.prefab.js +119 -0
  175. package/templates/create/3d-adventure/scenes/gameplay.scene.js +358 -0
  176. package/templates/create/3d-adventure/src/runtime/capabilities.js +56 -0
  177. package/templates/create/3d-adventure/src/runtime/materials.js +39 -0
  178. package/templates/create/3d-adventure/src/runtime/state.js +31 -0
  179. package/templates/create/3d-adventure/ui/hud.screen.js +70 -0
  180. package/templates/create/3d-adventure/ui/pause.screen.js +437 -0
  181. package/templates/create/3d-collectathon/assets/models/starter-avatar.gltf +184 -0
  182. package/templates/create/3d-collectathon/config/gameplay/.gitkeep +1 -0
  183. package/templates/create/3d-collectathon/content/gameplay/collectibles.json +26 -0
  184. package/templates/create/3d-collectathon/content/gameplay/course.js +46 -0
  185. package/templates/create/3d-collectathon/content/registries/.gitkeep +1 -0
  186. package/templates/create/3d-collectathon/docs/design/.gitkeep +1 -0
  187. package/templates/create/3d-collectathon/docs/design/loop.md +5 -0
  188. package/templates/create/3d-collectathon/prefabs/collectible.prefab.js +15 -0
  189. package/templates/create/3d-collectathon/prefabs/player.prefab.js +207 -0
  190. package/templates/create/3d-collectathon/prefabs/world.prefab.js +112 -0
  191. package/templates/create/3d-collectathon/scenes/boot.scene.js +12 -0
  192. package/templates/create/3d-collectathon/scenes/checkpoint.scene.js +9 -0
  193. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +200 -0
  194. package/templates/create/3d-collectathon/src/main.js +5 -355
  195. package/templates/create/3d-collectathon/src/runtime/app.js +49 -0
  196. package/templates/create/3d-collectathon/src/runtime/capabilities.js +53 -0
  197. package/templates/create/3d-collectathon/src/runtime/materials.js +34 -0
  198. package/templates/create/3d-collectathon/src/runtime/state.js +27 -0
  199. package/templates/create/3d-collectathon/ui/hud.screen.js +66 -0
  200. package/templates/create/3d-collectathon/ui/pause.screen.js +13 -0
  201. package/templates/create/blank/config/gameplay/.gitkeep +1 -0
  202. package/templates/create/blank/content/gameplay/.gitkeep +1 -0
  203. package/templates/create/blank/content/registries/.gitkeep +1 -0
  204. package/templates/create/blank/docs/design/.gitkeep +1 -0
  205. package/templates/create/blank/docs/design/loop.md +5 -0
  206. package/templates/create/blank/prefabs/.gitkeep +1 -0
  207. package/templates/create/blank/scenes/.gitkeep +1 -0
  208. package/templates/create/blank/src/runtime/.gitkeep +1 -0
  209. package/templates/create/blank/ui/.gitkeep +1 -0
  210. package/templates/create/deckbuilder-2d/assets/audio/.gitkeep +1 -0
  211. package/templates/create/deckbuilder-2d/assets/fonts/.gitkeep +1 -0
  212. package/templates/create/deckbuilder-2d/assets/sprites/.gitkeep +1 -0
  213. package/templates/create/deckbuilder-2d/assets/starter/README.md +11 -0
  214. package/templates/create/deckbuilder-2d/assets/ui/.gitkeep +1 -0
  215. package/templates/create/deckbuilder-2d/aura.config.json +28 -0
  216. package/templates/create/deckbuilder-2d/config/gameplay/deckbuilder.config.js +26 -0
  217. package/templates/create/deckbuilder-2d/content/cards/guard.card.js +19 -0
  218. package/templates/create/deckbuilder-2d/content/cards/spark.card.js +20 -0
  219. package/templates/create/deckbuilder-2d/content/cards/starter.deck.js +69 -0
  220. package/templates/create/deckbuilder-2d/content/cards/strike.card.js +19 -0
  221. package/templates/create/deckbuilder-2d/content/cards/survey.card.js +20 -0
  222. package/templates/create/deckbuilder-2d/content/encounters/training-battle.encounter.js +14 -0
  223. package/templates/create/deckbuilder-2d/content/encounters/training-battle.js +65 -0
  224. package/templates/create/deckbuilder-2d/content/enemies/training-automaton.enemy.js +48 -0
  225. package/templates/create/deckbuilder-2d/content/gameplay/.gitkeep +1 -0
  226. package/templates/create/deckbuilder-2d/content/registries/cards.registry.js +26 -0
  227. package/templates/create/deckbuilder-2d/content/registries/encounters.registry.js +20 -0
  228. package/templates/create/deckbuilder-2d/content/registries/enemies.registry.js +20 -0
  229. package/templates/create/deckbuilder-2d/content/registries/relics.registry.js +20 -0
  230. package/templates/create/deckbuilder-2d/content/relics/ember-charm.relic.js +18 -0
  231. package/templates/create/deckbuilder-2d/docs/design/loop.md +12 -0
  232. package/templates/create/deckbuilder-2d/prefabs/.gitkeep +1 -0
  233. package/templates/create/deckbuilder-2d/scenes/boot.scene.js +84 -0
  234. package/templates/create/deckbuilder-2d/scenes/gameplay.scene.js +641 -0
  235. package/templates/create/deckbuilder-2d/src/components/.gitkeep +1 -0
  236. package/templates/create/deckbuilder-2d/src/main.js +17 -0
  237. package/templates/create/deckbuilder-2d/src/runtime/capabilities.js +22 -0
  238. package/templates/create/deckbuilder-2d/src/shared/.gitkeep +1 -0
  239. package/templates/create/deckbuilder-2d/src/systems/.gitkeep +1 -0
  240. package/templates/create/deckbuilder-2d/tests/smoke/.gitkeep +1 -0
  241. package/templates/create/deckbuilder-2d/ui/hud.screen.js +80 -0
  242. package/templates/create/deckbuilder-2d/ui/pause.screen.js +146 -0
  243. package/templates/create/deckbuilder-2d/ui/settings.screen.js +342 -0
  244. package/templates/create/local-multiplayer/aura.config.json +40 -0
  245. package/templates/create/local-multiplayer/config/gameplay/local-multiplayer.config.js +26 -0
  246. package/templates/create/local-multiplayer/content/gameplay/room-layout.js +13 -0
  247. package/templates/create/local-multiplayer/content/registries/.gitkeep +1 -0
  248. package/templates/create/local-multiplayer/docs/design/loop.md +14 -0
  249. package/templates/create/local-multiplayer/prefabs/player.prefab.js +99 -0
  250. package/templates/create/local-multiplayer/scenes/boot.scene.js +12 -0
  251. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +443 -0
  252. package/templates/create/local-multiplayer/src/main.js +17 -0
  253. package/templates/create/local-multiplayer/src/runtime/capabilities.js +28 -0
  254. package/templates/create/local-multiplayer/ui/hud.screen.js +60 -0
  255. package/templates/create/shared/src/runtime/project-inspector.js +105 -0
  256. package/templates/create/shared/src/runtime/scene-flow.js +290 -0
  257. package/templates/create/shared/src/runtime/screen-shell.js +222 -0
  258. package/templates/create/shared/src/runtime/ui-forms.js +209 -0
  259. package/templates/create/shared/src/runtime/ui-settings.js +237 -0
  260. package/templates/create/shared/src/runtime/ui-theme.js +352 -0
  261. package/templates/create/shared/src/starter-utils/adventure-objectives.js +102 -0
  262. package/templates/create/shared/src/starter-utils/animation-2d.js +337 -0
  263. package/templates/create/shared/src/starter-utils/avatar-3d.js +404 -0
  264. package/templates/create/shared/src/starter-utils/combat-feedback-2d.js +320 -0
  265. package/templates/create/shared/src/starter-utils/core.js +39 -3
  266. package/templates/create/shared/src/starter-utils/index.js +8 -2
  267. package/templates/create/shared/src/starter-utils/platformer-3d.js +34 -3
  268. package/templates/create/shared/src/starter-utils/triggers.js +662 -0
  269. package/templates/create/shared/src/starter-utils/tween-2d.js +615 -0
  270. package/templates/create/video-cutscene/assets/video/.gitkeep +0 -0
  271. package/templates/create/video-cutscene/aura.config.json +28 -0
  272. package/templates/create/video-cutscene/config/gameplay/.gitkeep +0 -0
  273. package/templates/create/video-cutscene/content/gameplay/.gitkeep +0 -0
  274. package/templates/create/video-cutscene/content/registries/.gitkeep +0 -0
  275. package/templates/create/video-cutscene/docs/design/loop.md +22 -0
  276. package/templates/create/video-cutscene/prefabs/.gitkeep +0 -0
  277. package/templates/create/video-cutscene/scenes/boot.scene.js +11 -0
  278. package/templates/create/video-cutscene/scenes/cutscene.scene.js +113 -0
  279. package/templates/create/video-cutscene/scenes/gameplay.scene.js +50 -0
  280. package/templates/create/video-cutscene/src/main.js +17 -0
  281. package/templates/create/video-cutscene/src/runtime/app.js +52 -0
  282. package/templates/create/video-cutscene/src/runtime/capabilities.js +35 -0
  283. package/templates/create/video-cutscene/src/runtime/state.js +13 -0
  284. package/templates/create/video-cutscene/ui/.gitkeep +0 -0
  285. package/templates/create-bin/play.js +1187 -0
  286. package/templates/make/README.md +46 -0
  287. package/templates/make/catalog.json +51 -0
  288. package/templates/make/component/files/{{MAKE_NAME}}.component.js +20 -0
  289. package/templates/make/component/manifest.json +9 -0
  290. package/templates/make/data/files/{{MAKE_NAME}}.json +14 -0
  291. package/templates/make/data/manifest.json +9 -0
  292. package/templates/make/material/files/{{MAKE_NAME}}.material.json +17 -0
  293. package/templates/make/material/manifest.json +9 -0
  294. package/templates/make/prefab/files/{{MAKE_NAME}}.prefab.js +20 -0
  295. package/templates/make/prefab/manifest.json +9 -0
  296. package/templates/make/scene/files/{{MAKE_NAME}}.scene.js +31 -0
  297. package/templates/make/scene/manifest.json +9 -0
  298. package/templates/make/shader/files/{{MAKE_NAME}}.shader.js +23 -0
  299. package/templates/make/shader/manifest.json +9 -0
  300. package/templates/make/system/files/{{MAKE_NAME}}.system.js +15 -0
  301. package/templates/make/system/manifest.json +9 -0
  302. package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.js +16 -0
  303. package/templates/make/ui-screen/files/{{MAKE_NAME}}.screen.json +23 -0
  304. package/templates/make/ui-screen/manifest.json +10 -0
  305. package/templates/make-starters/deckbuilder-2d/card/files/{{MAKE_NAME}}.card.js +22 -0
  306. package/templates/make-starters/deckbuilder-2d/card/manifest.json +9 -0
  307. package/templates/make-starters/deckbuilder-2d/catalog.json +34 -0
  308. package/templates/make-starters/deckbuilder-2d/encounter/files/{{MAKE_NAME}}.encounter.js +18 -0
  309. package/templates/make-starters/deckbuilder-2d/encounter/manifest.json +9 -0
  310. package/templates/make-starters/deckbuilder-2d/enemy/files/{{MAKE_NAME}}.enemy.js +28 -0
  311. package/templates/make-starters/deckbuilder-2d/enemy/manifest.json +9 -0
  312. package/templates/make-starters/deckbuilder-2d/relic/files/{{MAKE_NAME}}.relic.js +23 -0
  313. package/templates/make-starters/deckbuilder-2d/relic/manifest.json +9 -0
  314. package/templates/retro/platformer/README.md +10 -0
  315. package/templates/retro/platformer/assets/retro/assets.json +91 -0
  316. package/templates/retro/platformer/aura.config.json +7 -0
  317. package/templates/retro/platformer/package.json +5 -0
  318. package/templates/retro/platformer/src/main.js +40 -0
  319. package/templates/retro/puzzle-grid/README.md +10 -0
  320. package/templates/retro/puzzle-grid/assets/retro/assets.json +90 -0
  321. package/templates/retro/puzzle-grid/aura.config.json +7 -0
  322. package/templates/retro/puzzle-grid/package.json +5 -0
  323. package/templates/retro/puzzle-grid/src/main.js +29 -0
  324. package/templates/retro/tactics-grid/README.md +10 -0
  325. package/templates/retro/tactics-grid/assets/retro/assets.json +90 -0
  326. package/templates/retro/tactics-grid/aura.config.json +7 -0
  327. package/templates/retro/tactics-grid/package.json +5 -0
  328. package/templates/retro/tactics-grid/src/main.js +35 -0
  329. package/templates/retro/topdown-adventure/README.md +10 -0
  330. package/templates/retro/topdown-adventure/assets/retro/assets.json +95 -0
  331. package/templates/retro/topdown-adventure/aura.config.json +7 -0
  332. package/templates/retro/topdown-adventure/package.json +5 -0
  333. package/templates/retro/topdown-adventure/src/main.js +29 -0
  334. package/templates/skills/aurajs/SKILL.md +61 -5
@@ -0,0 +1,2016 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const CLI_SRC_DIR = dirname(fileURLToPath(import.meta.url));
6
+ const SHARED_RUNTIME_TEMPLATE_DIR = resolve(CLI_SRC_DIR, '../templates/create/shared/src/runtime');
7
+
8
+ export const PROJECT_MAIN_ENTRY_RELATIVE_PATH = 'src/main.js';
9
+ export const PROJECT_RUNTIME_APP_RELATIVE_PATH = 'src/runtime/app.js';
10
+ export const PROJECT_RUNTIME_APP_STATE_RELATIVE_PATH = 'src/runtime/app-state.js';
11
+ export const PROJECT_RUNTIME_CAPABILITIES_RELATIVE_PATH = 'src/runtime/capabilities.js';
12
+ export const PROJECT_RUNTIME_INSPECTOR_RELATIVE_PATH = 'src/runtime/project-inspector.js';
13
+ export const PROJECT_RUNTIME_SCENE_FLOW_RELATIVE_PATH = 'src/runtime/scene-flow.js';
14
+ export const PROJECT_RUNTIME_SCREEN_SHELL_RELATIVE_PATH = 'src/runtime/screen-shell.js';
15
+ export const PROJECT_RUNTIME_UI_THEME_RELATIVE_PATH = 'src/runtime/ui-theme.js';
16
+ export const PROJECT_RUNTIME_UI_SETTINGS_RELATIVE_PATH = 'src/runtime/ui-settings.js';
17
+ export const PROJECT_RUNTIME_UI_FORMS_RELATIVE_PATH = 'src/runtime/ui-forms.js';
18
+ export const PROJECT_INSPECTOR_SCHEMA = 'aurajs.project-inspector.v1';
19
+ export const PROJECT_STATE_SCHEMA = 'aurajs.authored-project-state.v1';
20
+ export const APP_STATE_SCHEMA = 'aurajs.app-state.v1';
21
+
22
+ function readSharedRuntimeTemplate(relativePath) {
23
+ return readFileSync(resolve(SHARED_RUNTIME_TEMPLATE_DIR, relativePath), 'utf8');
24
+ }
25
+
26
+ export function renderSceneFlowModule() {
27
+ return readSharedRuntimeTemplate('scene-flow.js');
28
+ }
29
+
30
+ export function renderScreenShellModule() {
31
+ return readSharedRuntimeTemplate('screen-shell.js');
32
+ }
33
+
34
+ export function renderUiThemeModule() {
35
+ return readSharedRuntimeTemplate('ui-theme.js');
36
+ }
37
+
38
+ export function renderUiSettingsModule() {
39
+ return readSharedRuntimeTemplate('ui-settings.js');
40
+ }
41
+
42
+ export function renderUiFormsModule() {
43
+ return readSharedRuntimeTemplate('ui-forms.js');
44
+ }
45
+
46
+ export function renderAppStateModule() {
47
+ return `export const APP_STATE_SCHEMA = ${JSON.stringify(APP_STATE_SCHEMA)};
48
+
49
+ function cloneAppStateValue(value) {
50
+ if (Array.isArray(value)) {
51
+ return value.map((entry) => cloneAppStateValue(entry));
52
+ }
53
+ if (value && typeof value === 'object') {
54
+ const output = {};
55
+ for (const [key, entry] of Object.entries(value)) {
56
+ output[key] = cloneAppStateValue(entry);
57
+ }
58
+ return output;
59
+ }
60
+ return value;
61
+ }
62
+
63
+ function createStoreSection() {
64
+ return {};
65
+ }
66
+
67
+ export function createAppState({ projectTitle = 'AuraJS Game', template = 'blank' } = {}) {
68
+ return {
69
+ schema: APP_STATE_SCHEMA,
70
+ projectTitle,
71
+ template,
72
+ session: createStoreSection(),
73
+ ui: createStoreSection(),
74
+ runtime: {
75
+ startSceneId: null,
76
+ currentSceneId: null,
77
+ previousSceneId: null,
78
+ hudScreenId: null,
79
+ overlayScreenId: null,
80
+ modalScreenIds: [],
81
+ },
82
+ };
83
+ }
84
+
85
+ function ensureStoreBucket(appState, bucketName) {
86
+ if (!appState || typeof appState !== 'object' || Array.isArray(appState)) {
87
+ throw new Error('Expected appState to be a mutable object.');
88
+ }
89
+ if (!appState[bucketName] || typeof appState[bucketName] !== 'object' || Array.isArray(appState[bucketName])) {
90
+ appState[bucketName] = createStoreSection();
91
+ }
92
+ return appState[bucketName];
93
+ }
94
+
95
+ export function ensureSessionState(appState, featureKey, seed = {}) {
96
+ const key = typeof featureKey === 'string' ? featureKey.trim() : '';
97
+ if (!key) {
98
+ throw new Error('Expected a non-empty session feature key.');
99
+ }
100
+ const session = ensureStoreBucket(appState, 'session');
101
+ if (!session[key] || typeof session[key] !== 'object' || Array.isArray(session[key])) {
102
+ session[key] = cloneAppStateValue(seed);
103
+ }
104
+ return session[key];
105
+ }
106
+
107
+ export function ensureUiState(appState, featureKey, seed = {}) {
108
+ const key = typeof featureKey === 'string' ? featureKey.trim() : '';
109
+ if (!key) {
110
+ throw new Error('Expected a non-empty UI feature key.');
111
+ }
112
+ const ui = ensureStoreBucket(appState, 'ui');
113
+ if (!ui[key] || typeof ui[key] !== 'object' || Array.isArray(ui[key])) {
114
+ ui[key] = cloneAppStateValue(seed);
115
+ }
116
+ return ui[key];
117
+ }
118
+
119
+ export function normalizeAppState(appState, { projectTitle = 'AuraJS Game', template = 'blank' } = {}) {
120
+ const normalized = appState && typeof appState === 'object' && !Array.isArray(appState)
121
+ ? appState
122
+ : createAppState({ projectTitle, template });
123
+ normalized.schema = APP_STATE_SCHEMA;
124
+ normalized.projectTitle = projectTitle;
125
+ normalized.template = template;
126
+ ensureStoreBucket(normalized, 'session');
127
+ ensureStoreBucket(normalized, 'ui');
128
+ const runtimeState = normalized.runtime && typeof normalized.runtime === 'object' && !Array.isArray(normalized.runtime)
129
+ ? normalized.runtime
130
+ : {};
131
+ normalized.runtime = {
132
+ startSceneId: null,
133
+ currentSceneId: null,
134
+ previousSceneId: null,
135
+ hudScreenId: null,
136
+ overlayScreenId: null,
137
+ modalScreenIds: [],
138
+ ...runtimeState,
139
+ modalScreenIds: Array.isArray(runtimeState.modalScreenIds)
140
+ ? runtimeState.modalScreenIds
141
+ .map((entry) => (typeof entry === 'string' ? entry.trim() : ''))
142
+ .filter((entry) => entry.length > 0)
143
+ : [],
144
+ };
145
+ return normalized;
146
+ }
147
+ `;
148
+ }
149
+
150
+ export function renderProjectInspectorModule() {
151
+ return `// Project Inspector overlay for registry-backed authored projects.
152
+ import projectRegistry from './project-registry.js';
153
+ import { ensureUiState } from './app-state.js';
154
+
155
+ const PROJECT_INSPECTOR_SCHEMA = ${JSON.stringify(PROJECT_INSPECTOR_SCHEMA)};
156
+ const PROJECT_INSPECTOR_TOGGLE_KEY = 'F1';
157
+ const HUD_MARGIN = 12;
158
+ const HUD_PADDING = 10;
159
+ const HUD_ROW_GAP = 8;
160
+ const HUD_SCENE_ROW_HEIGHT = 28;
161
+ const HUD_TOOLBAR_ROW_HEIGHT = 28;
162
+ const HUD_CHIP_GAP = 8;
163
+ const HUD_MINIMIZED_HEIGHT = 30;
164
+ const HUD_MINIMIZED_WIDTH = 260;
165
+ const HUD_MINIMIZED_MAX_WIDTH = 420;
166
+
167
+ function summarizeEntries(entries, formatter) {
168
+ const list = Array.isArray(entries) ? entries : [];
169
+ if (list.length === 0) return '(none)';
170
+ return list.map((entry) => formatter(entry)).join(', ');
171
+ }
172
+
173
+ function hasMethod(obj, method) {
174
+ return Boolean(obj) && typeof obj[method] === 'function';
175
+ }
176
+
177
+ function getRuntime() {
178
+ return globalThis.aura || null;
179
+ }
180
+
181
+ function getWindowSize() {
182
+ const runtime = getRuntime();
183
+ if (hasMethod(runtime?.window, 'getSize')) {
184
+ return runtime.window.getSize();
185
+ }
186
+ return { width: 1280, height: 720 };
187
+ }
188
+
189
+ function inputIsKeyPressed(input, key) {
190
+ return hasMethod(input, 'isKeyPressed') && input.isKeyPressed(String(key || '').toLowerCase());
191
+ }
192
+
193
+ function mousePressed(input, name = 'left', fallbackIndex = 0) {
194
+ if (hasMethod(input?.mouse, 'isPressed')) {
195
+ return input.mouse.isPressed(name);
196
+ }
197
+ if (hasMethod(input, 'isMousePressed')) {
198
+ return input.isMousePressed(fallbackIndex);
199
+ }
200
+ return false;
201
+ }
202
+
203
+ function mousePosition(input) {
204
+ if (hasMethod(input, 'getMousePosition')) {
205
+ const point = input.getMousePosition();
206
+ return {
207
+ x: Number.isFinite(point?.x) ? Number(point.x) : 0,
208
+ y: Number.isFinite(point?.y) ? Number(point.y) : 0,
209
+ };
210
+ }
211
+ return {
212
+ x: Number.isFinite(input?.mouse?.x) ? Number(input.mouse.x) : 0,
213
+ y: Number.isFinite(input?.mouse?.y) ? Number(input.mouse.y) : 0,
214
+ };
215
+ }
216
+
217
+ function pointInRect(point, rect) {
218
+ if (!point || !rect) return false;
219
+ return point.x >= rect.x
220
+ && point.x <= rect.x + rect.width
221
+ && point.y >= rect.y
222
+ && point.y <= rect.y + rect.height;
223
+ }
224
+
225
+ function approximateChipWidth(label) {
226
+ return Math.max(76, 18 + (String(label || '').length * 8));
227
+ }
228
+
229
+ function hudStatusFlags(snapshot) {
230
+ const flags = [];
231
+ if (snapshot.recording?.active === true) {
232
+ flags.push('REC');
233
+ }
234
+ if (snapshot.paused === true) {
235
+ flags.push('PAUSED');
236
+ }
237
+ return flags;
238
+ }
239
+
240
+ function buildMinimizedHudLabel(snapshot) {
241
+ const parts = [
242
+ \`\${PROJECT_INSPECTOR_TOGGLE_KEY} Play HUD\`,
243
+ snapshot.activeSceneId || '(none)',
244
+ ...hudStatusFlags(snapshot),
245
+ ];
246
+ return parts.join(' • ');
247
+ }
248
+
249
+ function buildExpandedHudStatusLabel(snapshot) {
250
+ const parts = [
251
+ \`Scene \${snapshot.activeSceneId || '(none)'}\`,
252
+ snapshot.theme?.presetId ? \`Theme \${snapshot.theme.presetId}\` : null,
253
+ ...hudStatusFlags(snapshot),
254
+ ].filter(Boolean);
255
+ return parts.join(' • ');
256
+ }
257
+
258
+ function buildToolbarActions({
259
+ canRecord = false,
260
+ recordingActive = false,
261
+ canScreenshot = false,
262
+ canRestartScene = false,
263
+ canRestartApp = false,
264
+ canPause = false,
265
+ paused = false,
266
+ } = {}) {
267
+ return [
268
+ {
269
+ id: 'record',
270
+ label: recordingActive ? 'Stop Rec' : 'Record',
271
+ enabled: canRecord,
272
+ active: recordingActive,
273
+ },
274
+ {
275
+ id: 'screenshot',
276
+ label: 'Screenshot',
277
+ enabled: canScreenshot,
278
+ active: false,
279
+ },
280
+ {
281
+ id: 'restart-scene',
282
+ label: 'Restart Scene',
283
+ enabled: canRestartScene,
284
+ active: false,
285
+ },
286
+ {
287
+ id: 'restart-app',
288
+ label: 'Restart App',
289
+ enabled: canRestartApp,
290
+ active: false,
291
+ },
292
+ {
293
+ id: 'pause',
294
+ label: paused ? 'Resume' : 'Pause',
295
+ enabled: canPause,
296
+ active: paused,
297
+ },
298
+ {
299
+ id: 'minimize',
300
+ label: 'Minimize',
301
+ enabled: true,
302
+ active: false,
303
+ },
304
+ ];
305
+ }
306
+
307
+ function drawChip(draw2d, colorFactory, chip, options = {}) {
308
+ const enabled = chip.enabled !== false;
309
+ const active = options.active === true;
310
+ const background = !enabled
311
+ ? colorFactory(0.18, 0.2, 0.24, 0.72)
312
+ : active
313
+ ? colorFactory(0.28, 0.54, 0.92, 0.94)
314
+ : colorFactory(0.11, 0.15, 0.2, 0.92);
315
+ const border = !enabled
316
+ ? colorFactory(0.28, 0.3, 0.34, 0.94)
317
+ : active
318
+ ? colorFactory(0.94, 0.82, 0.32, 0.96)
319
+ : colorFactory(0.38, 0.76, 1, 0.82);
320
+ const textColor = !enabled
321
+ ? colorFactory(0.62, 0.66, 0.72, 0.92)
322
+ : colorFactory(0.96, 0.98, 1, 1);
323
+
324
+ draw2d.rect(chip.x, chip.y, chip.width, chip.height, background);
325
+ draw2d.rect(chip.x, chip.y, chip.width, 2, border);
326
+ draw2d.text(chip.label, chip.x + 10, chip.y + 7, {
327
+ size: 12,
328
+ color: textColor,
329
+ });
330
+ }
331
+
332
+ function drawSceneStrip(draw2d, colorFactory, snapshot, panelX, panelY, panelWidth, uiState = null) {
333
+ const chips = [];
334
+ let x = panelX + HUD_PADDING;
335
+ const y = panelY + 22;
336
+ const maxX = panelX + panelWidth - HUD_PADDING;
337
+ const scenes = Array.isArray(snapshot.scenes) ? snapshot.scenes : [];
338
+ for (const scene of scenes) {
339
+ const label = scene.id || '(scene)';
340
+ const width = approximateChipWidth(label);
341
+ if (x + width > maxX) {
342
+ const remaining = scenes.length - chips.length;
343
+ if (remaining > 0) {
344
+ const overflowLabel = '+' + String(remaining);
345
+ const overflowWidth = approximateChipWidth(overflowLabel);
346
+ chips.push({
347
+ id: '__overflow__',
348
+ label: overflowLabel,
349
+ x,
350
+ y,
351
+ width: Math.min(overflowWidth, Math.max(60, maxX - x)),
352
+ height: HUD_SCENE_ROW_HEIGHT,
353
+ enabled: false,
354
+ active: false,
355
+ });
356
+ }
357
+ break;
358
+ }
359
+ chips.push({
360
+ id: scene.id,
361
+ label,
362
+ x,
363
+ y,
364
+ width,
365
+ height: HUD_SCENE_ROW_HEIGHT,
366
+ enabled: true,
367
+ active: scene.active === true,
368
+ });
369
+ x += width + HUD_CHIP_GAP;
370
+ }
371
+
372
+ for (const chip of chips) {
373
+ drawChip(draw2d, colorFactory, chip, { active: chip.active });
374
+ }
375
+ if (uiState) {
376
+ uiState.sceneChips = chips.map((chip) => ({ ...chip }));
377
+ }
378
+ }
379
+
380
+ function layoutToolbar(snapshot, panelX, panelY, panelWidth) {
381
+ const buttons = [];
382
+ let x = panelX + HUD_PADDING;
383
+ const y = panelY + 22 + HUD_SCENE_ROW_HEIGHT + HUD_ROW_GAP;
384
+ const maxX = panelX + panelWidth - HUD_PADDING;
385
+ const actions = Array.isArray(snapshot.toolbarActions) ? snapshot.toolbarActions : [];
386
+ for (const action of actions) {
387
+ const width = approximateChipWidth(action.label);
388
+ if (x + width > maxX) break;
389
+ buttons.push({
390
+ id: action.id,
391
+ label: action.label,
392
+ enabled: action.enabled !== false,
393
+ active: action.active === true,
394
+ x,
395
+ y,
396
+ width,
397
+ height: HUD_TOOLBAR_ROW_HEIGHT,
398
+ });
399
+ x += width + HUD_CHIP_GAP;
400
+ }
401
+ return buttons;
402
+ }
403
+
404
+ function drawProjectInspectorOverlay(snapshot, uiState) {
405
+ const runtime = getRuntime();
406
+ if (!runtime?.draw2d || typeof runtime.draw2d.rect !== 'function' || typeof runtime.draw2d.text !== 'function' || typeof runtime.rgba !== 'function') {
407
+ return;
408
+ }
409
+
410
+ const windowSize = getWindowSize();
411
+ const panelX = HUD_MARGIN;
412
+ const panelY = HUD_MARGIN;
413
+ const panelWidth = Math.max(420, Math.min(1240, Number(windowSize?.width || 1280) - (HUD_MARGIN * 2)));
414
+
415
+ if (snapshot.presentation === 'minimized') {
416
+ const label = buildMinimizedHudLabel(snapshot);
417
+ const chip = {
418
+ x: panelX,
419
+ y: panelY,
420
+ width: Math.min(HUD_MINIMIZED_MAX_WIDTH, Math.max(HUD_MINIMIZED_WIDTH, approximateChipWidth(label) + 22)),
421
+ height: HUD_MINIMIZED_HEIGHT,
422
+ label,
423
+ enabled: true,
424
+ active: snapshot.paused === true || snapshot.recording?.active === true,
425
+ };
426
+ drawChip(runtime.draw2d, runtime.rgba, chip, { active: chip.active });
427
+ uiState.minimizedChip = chip;
428
+ uiState.toolbarButtons = [];
429
+ uiState.sceneChips = [];
430
+ return;
431
+ }
432
+
433
+ const panelHeight = 22 + HUD_SCENE_ROW_HEIGHT + HUD_ROW_GAP + HUD_TOOLBAR_ROW_HEIGHT + HUD_PADDING;
434
+
435
+ runtime.draw2d.rect(panelX, panelY, panelWidth, panelHeight, runtime.rgba(0.03, 0.05, 0.08, 0.92));
436
+ runtime.draw2d.rect(panelX, panelY, panelWidth, 3, runtime.rgba(0.38, 0.76, 1.0, 0.92));
437
+ runtime.draw2d.text(\`\${PROJECT_INSPECTOR_TOGGLE_KEY} Play HUD\`, panelX + HUD_PADDING, panelY + 6, {
438
+ size: 13,
439
+ color: runtime.rgba(1, 0.97, 0.86, 1),
440
+ });
441
+ runtime.draw2d.text(buildExpandedHudStatusLabel(snapshot), panelX + panelWidth - 240, panelY + 6, {
442
+ size: 12,
443
+ color: runtime.rgba(0.88, 0.94, 1, 1),
444
+ });
445
+
446
+ drawSceneStrip(runtime.draw2d, runtime.rgba, snapshot, panelX, panelY, panelWidth, uiState);
447
+ uiState.toolbarButtons = layoutToolbar(snapshot, panelX, panelY, panelWidth);
448
+ for (const button of uiState.toolbarButtons) {
449
+ drawChip(runtime.draw2d, runtime.rgba, button, { active: button.active });
450
+ }
451
+ uiState.minimizedChip = null;
452
+ }
453
+
454
+ function cloneInspectorValue(value) {
455
+ if (Array.isArray(value)) {
456
+ return value.map((entry) => cloneInspectorValue(entry));
457
+ }
458
+ if (value && typeof value === 'object') {
459
+ const output = {};
460
+ for (const [key, entry] of Object.entries(value)) {
461
+ output[key] = cloneInspectorValue(entry);
462
+ }
463
+ return output;
464
+ }
465
+ return value;
466
+ }
467
+
468
+ export function buildProjectInspectorSnapshot({
469
+ projectTitle = null,
470
+ sceneRegistry = null,
471
+ getActiveSceneId = null,
472
+ appState = null,
473
+ getUiThemeState = null,
474
+ enabled = false,
475
+ minimized = false,
476
+ paused = false,
477
+ recording = null,
478
+ actions = null,
479
+ } = {}) {
480
+ const activeSceneId = typeof getActiveSceneId === 'function' ? getActiveSceneId() : null;
481
+ const registryScenes = Array.isArray(projectRegistry.scenes) ? projectRegistry.scenes : [];
482
+ const registryScreens = Array.isArray(projectRegistry.screens) ? projectRegistry.screens : [];
483
+ const registryPrefabs = Array.isArray(projectRegistry.prefabs) ? projectRegistry.prefabs : [];
484
+ const registryDataFiles = Array.isArray(projectRegistry.dataFiles) ? projectRegistry.dataFiles : [];
485
+ const runtimeSceneIds = sceneRegistry && typeof sceneRegistry === 'object' && sceneRegistry.scenes
486
+ ? Object.keys(sceneRegistry.scenes)
487
+ : [];
488
+ const sessionState = appState && typeof appState?.session === 'object' && !Array.isArray(appState.session)
489
+ ? appState.session
490
+ : {};
491
+ const uiState = appState && typeof appState?.ui === 'object' && !Array.isArray(appState.ui)
492
+ ? appState.ui
493
+ : {};
494
+ const runtimeState = appState && typeof appState?.runtime === 'object' && !Array.isArray(appState.runtime)
495
+ ? appState.runtime
496
+ : {};
497
+ const themeState = typeof getUiThemeState === 'function' ? getUiThemeState() : null;
498
+ const uiPreferences = uiState.preferences && typeof uiState.preferences === 'object' && !Array.isArray(uiState.preferences)
499
+ ? cloneInspectorValue(uiState.preferences)
500
+ : null;
501
+
502
+ return {
503
+ schema: PROJECT_INSPECTOR_SCHEMA,
504
+ template: projectRegistry.template || 'blank',
505
+ projectTitle: projectTitle || projectRegistry.projectTitle || 'AuraJS Game',
506
+ enabled: enabled === true,
507
+ presentation: minimized === true ? 'minimized' : 'expanded',
508
+ toggleKey: PROJECT_INSPECTOR_TOGGLE_KEY,
509
+ sourceOfTruth: 'src/runtime/project-registry.js',
510
+ startSceneId: projectRegistry.startSceneId || null,
511
+ activeSceneId: activeSceneId || null,
512
+ paused: paused === true,
513
+ recording: recording && typeof recording === 'object'
514
+ ? {
515
+ supported: recording.supported === true,
516
+ active: recording.active === true,
517
+ }
518
+ : {
519
+ supported: false,
520
+ active: false,
521
+ },
522
+ runtimeSceneIds,
523
+ appStateKeys: appState && typeof appState === 'object' && !Array.isArray(appState)
524
+ ? Object.keys(appState).sort()
525
+ : [],
526
+ appStateBuckets: ['session', 'ui', 'runtime'],
527
+ sessionKeys: Object.keys(sessionState).sort(),
528
+ uiKeys: Object.keys(uiState).sort(),
529
+ runtimeKeys: Object.keys(runtimeState).sort(),
530
+ uiPreferencesPath: 'appState.ui.preferences',
531
+ uiPreferences,
532
+ theme: themeState && typeof themeState === 'object'
533
+ ? {
534
+ presetId: typeof themeState.presetId === 'string' ? themeState.presetId : null,
535
+ presetLabel: typeof themeState.presetLabel === 'string' ? themeState.presetLabel : null,
536
+ density: typeof themeState.density === 'string' ? themeState.density : null,
537
+ contrast: typeof themeState.contrast === 'string' ? themeState.contrast : null,
538
+ textScale: typeof themeState.textScale === 'string' ? themeState.textScale : null,
539
+ focusVisibility: typeof themeState.focusVisibility === 'string' ? themeState.focusVisibility : null,
540
+ availableThemeIds: Array.isArray(themeState.availableThemeIds) ? [...themeState.availableThemeIds] : [],
541
+ }
542
+ : null,
543
+ mountedScreens: {
544
+ hud: typeof runtimeState.hudScreenId === 'string' ? runtimeState.hudScreenId : null,
545
+ overlay: typeof runtimeState.overlayScreenId === 'string' ? runtimeState.overlayScreenId : null,
546
+ modals: Array.isArray(runtimeState.modalScreenIds) ? [...runtimeState.modalScreenIds] : [],
547
+ },
548
+ stateOwnership: {
549
+ engineApi: 'aura',
550
+ sharedSession: 'appState.session',
551
+ sharedUi: 'appState.ui',
552
+ sharedUiPreferences: 'appState.ui.preferences',
553
+ runtimeFacts: 'appState.runtime',
554
+ sceneLocal: 'sceneState',
555
+ sceneEntryPayload: 'sceneFlow.current.data',
556
+ screenPayloads: 'screenShell.hud|overlay|modals[].data',
557
+ themeModule: 'src/runtime/ui-theme.js',
558
+ retainedSettingsHelpers: 'src/runtime/ui-settings.js',
559
+ retainedFormsHelpers: 'src/runtime/ui-forms.js',
560
+ authoredInput: 'config/* and content/*',
561
+ },
562
+ scenes: registryScenes.map((entry) => ({
563
+ id: entry.id,
564
+ path: entry.path,
565
+ active: entry.id === activeSceneId,
566
+ })),
567
+ screens: registryScreens.map((entry) => ({
568
+ id: entry.id,
569
+ path: entry.path,
570
+ })),
571
+ prefabs: registryPrefabs.map((entry) => ({
572
+ id: entry.id,
573
+ role: entry.role || null,
574
+ path: entry.path,
575
+ })),
576
+ dataFiles: [...registryDataFiles],
577
+ toolbarActions: Array.isArray(actions) ? actions.map((entry) => ({ ...entry })) : [],
578
+ };
579
+ }
580
+
581
+ export function createProjectInspector({
582
+ projectTitle = null,
583
+ sceneRegistry = null,
584
+ getActiveSceneId = null,
585
+ appState = null,
586
+ getUiThemeState = null,
587
+ onSelectScene = null,
588
+ onRestartScene = null,
589
+ onRestartApp = null,
590
+ getPaused = null,
591
+ togglePaused = null,
592
+ getRecordingState = null,
593
+ toggleRecording = null,
594
+ } = {}) {
595
+ const playHudState = ensureUiState(appState, 'playHud', {
596
+ enabled: false,
597
+ minimized: false,
598
+ });
599
+ let enabled = playHudState.enabled === true;
600
+ let minimized = playHudState.minimized === true;
601
+ let pointerPressedLastFrame = false;
602
+ const uiState = {
603
+ sceneChips: [],
604
+ toolbarButtons: [],
605
+ minimizedChip: null,
606
+ };
607
+
608
+ function syncPlayHudState() {
609
+ playHudState.enabled = enabled === true;
610
+ playHudState.minimized = minimized === true;
611
+ }
612
+
613
+ function recordingState() {
614
+ if (typeof getRecordingState !== 'function') {
615
+ return { supported: false, active: false };
616
+ }
617
+ const state = getRecordingState();
618
+ return {
619
+ supported: state?.supported === true,
620
+ active: state?.active === true,
621
+ };
622
+ }
623
+
624
+ function currentActions() {
625
+ const runtime = getRuntime();
626
+ return buildToolbarActions({
627
+ canRecord: typeof toggleRecording === 'function' && recordingState().supported === true,
628
+ recordingActive: recordingState().active === true,
629
+ canScreenshot: hasMethod(runtime?.window, 'screenshot'),
630
+ canRestartScene: typeof onRestartScene === 'function',
631
+ canRestartApp: typeof onRestartApp === 'function',
632
+ canPause: typeof togglePaused === 'function',
633
+ paused: typeof getPaused === 'function' ? getPaused() === true : false,
634
+ });
635
+ }
636
+
637
+ function runToolbarAction(actionId) {
638
+ const runtime = getRuntime();
639
+ switch (actionId) {
640
+ case 'record':
641
+ if (typeof toggleRecording === 'function') {
642
+ toggleRecording();
643
+ }
644
+ return true;
645
+ case 'screenshot':
646
+ if (hasMethod(runtime?.window, 'screenshot')) {
647
+ runtime.window.screenshot();
648
+ return true;
649
+ }
650
+ return false;
651
+ case 'restart-scene':
652
+ if (typeof onRestartScene === 'function') {
653
+ onRestartScene();
654
+ return true;
655
+ }
656
+ return false;
657
+ case 'restart-app':
658
+ if (typeof onRestartApp === 'function') {
659
+ onRestartApp();
660
+ return true;
661
+ }
662
+ return false;
663
+ case 'pause':
664
+ if (typeof togglePaused === 'function') {
665
+ togglePaused();
666
+ return true;
667
+ }
668
+ return false;
669
+ case 'minimize':
670
+ minimized = !minimized;
671
+ syncPlayHudState();
672
+ return true;
673
+ default:
674
+ return false;
675
+ }
676
+ }
677
+
678
+ function runSceneSelection(sceneId) {
679
+ if (typeof onSelectScene !== 'function' || sceneId === '__overflow__') {
680
+ return false;
681
+ }
682
+ const result = onSelectScene(sceneId);
683
+ return result !== false && result?.ok !== false;
684
+ }
685
+
686
+ return {
687
+ get enabled() {
688
+ return enabled;
689
+ },
690
+ get minimized() {
691
+ return minimized;
692
+ },
693
+ show() {
694
+ enabled = true;
695
+ syncPlayHudState();
696
+ return enabled;
697
+ },
698
+ hide() {
699
+ enabled = false;
700
+ syncPlayHudState();
701
+ return enabled;
702
+ },
703
+ setEnabled(nextValue) {
704
+ enabled = nextValue === true;
705
+ syncPlayHudState();
706
+ return enabled;
707
+ },
708
+ toggle() {
709
+ enabled = !enabled;
710
+ syncPlayHudState();
711
+ return enabled;
712
+ },
713
+ setMinimized(nextValue) {
714
+ minimized = nextValue === true;
715
+ syncPlayHudState();
716
+ return minimized;
717
+ },
718
+ toggleMinimized() {
719
+ minimized = !minimized;
720
+ syncPlayHudState();
721
+ return minimized;
722
+ },
723
+ update(input = getRuntime()?.input || null) {
724
+ const pointerPressed = mousePressed(input, 'left', 0);
725
+ const pointerJustPressed = pointerPressed && !pointerPressedLastFrame;
726
+ pointerPressedLastFrame = pointerPressed;
727
+ if (inputIsKeyPressed(input, 'f1')) {
728
+ enabled = !enabled;
729
+ syncPlayHudState();
730
+ return enabled;
731
+ }
732
+ if (!enabled) {
733
+ return enabled;
734
+ }
735
+ if (inputIsKeyPressed(input, 'f2')) {
736
+ runToolbarAction('screenshot');
737
+ }
738
+ if (inputIsKeyPressed(input, 'p')) {
739
+ runToolbarAction('pause');
740
+ }
741
+ if (pointerJustPressed) {
742
+ const point = mousePosition(input);
743
+ if (minimized && pointInRect(point, uiState.minimizedChip)) {
744
+ minimized = false;
745
+ syncPlayHudState();
746
+ return enabled;
747
+ }
748
+ for (const chip of uiState.sceneChips) {
749
+ if (chip.enabled !== false && chip.active !== true && pointInRect(point, chip)) {
750
+ runSceneSelection(chip.id);
751
+ return enabled;
752
+ }
753
+ }
754
+ for (const button of uiState.toolbarButtons) {
755
+ if (button.enabled !== false && pointInRect(point, button)) {
756
+ runToolbarAction(button.id);
757
+ break;
758
+ }
759
+ }
760
+ }
761
+ return enabled;
762
+ },
763
+ snapshot() {
764
+ return buildProjectInspectorSnapshot({
765
+ projectTitle,
766
+ sceneRegistry,
767
+ getActiveSceneId,
768
+ appState,
769
+ getUiThemeState,
770
+ enabled,
771
+ minimized,
772
+ paused: typeof getPaused === 'function' ? getPaused() === true : false,
773
+ recording: recordingState(),
774
+ actions: currentActions(),
775
+ });
776
+ },
777
+ draw() {
778
+ if (!enabled) return;
779
+ drawProjectInspectorOverlay(this.snapshot(), uiState);
780
+ },
781
+ };
782
+ }
783
+ `;
784
+ }
785
+
786
+ export function renderRegistryBackedRuntimeApp({ template }) {
787
+ return `import {
788
+ createAppState,
789
+ ensureSessionState,
790
+ ensureUiState,
791
+ normalizeAppState,
792
+ } from './app-state.js';
793
+ import {
794
+ applyUiTheme as applySharedUiTheme,
795
+ ensureUiPreferences as ensureSharedUiPreferences,
796
+ inspectUiTheme as inspectSharedUiTheme,
797
+ resetUiPreferences as resetSharedUiPreferences,
798
+ updateUiPreferences as updateSharedUiPreferences,
799
+ } from './ui-theme.js';
800
+ import projectRegistry, { resolveProjectScreen } from './project-registry.js';
801
+ import { createProjectInspector } from './project-inspector.js';
802
+ import { createSceneRegistry } from './scene-registry.js';
803
+ import { createSceneFlow } from './scene-flow.js';
804
+ import { createScreenShell } from './screen-shell.js';
805
+ import { assertRuntimeCapabilities } from './capabilities.js';
806
+
807
+ const PROJECT_STATE_SCHEMA = ${JSON.stringify(PROJECT_STATE_SCHEMA)};
808
+ const PROJECT_CONTINUITY_STATE_SCHEMA = 'aurajs.project-continuity-state.v1';
809
+ const PROJECT_CONTINUITY_STATE_VERSION = 1;
810
+
811
+ function cloneProjectValue(value) {
812
+ if (Array.isArray(value)) {
813
+ return value.map((entry) => cloneProjectValue(entry));
814
+ }
815
+ if (value && typeof value === 'object') {
816
+ const output = {};
817
+ for (const [key, entry] of Object.entries(value)) {
818
+ output[key] = cloneProjectValue(entry);
819
+ }
820
+ return output;
821
+ }
822
+ return value;
823
+ }
824
+
825
+ function captureSceneSnapshot(scene, sceneId = null) {
826
+ if (!scene || typeof scene !== 'object') {
827
+ return {
828
+ ok: false,
829
+ sceneId: sceneId || null,
830
+ reasonCode: 'project_state_scene_missing',
831
+ detail: null,
832
+ hasSnapshot: false,
833
+ snapshot: null,
834
+ };
835
+ }
836
+ const exportFn = typeof scene.exportState === 'function'
837
+ ? scene.exportState
838
+ : typeof scene.getState === 'function'
839
+ ? scene.getState
840
+ : null;
841
+ if (!exportFn) {
842
+ return {
843
+ ok: true,
844
+ sceneId: sceneId || null,
845
+ reasonCode: 'project_state_scene_skipped',
846
+ detail: null,
847
+ hasSnapshot: false,
848
+ snapshot: null,
849
+ };
850
+ }
851
+ try {
852
+ const snapshot = exportFn.call(scene);
853
+ return {
854
+ ok: true,
855
+ sceneId: sceneId || null,
856
+ reasonCode: snapshot == null ? 'project_state_scene_empty' : 'project_state_scene_capture_ok',
857
+ detail: null,
858
+ hasSnapshot: snapshot != null,
859
+ snapshot: snapshot == null ? null : cloneProjectValue(snapshot),
860
+ };
861
+ } catch (error) {
862
+ return {
863
+ ok: false,
864
+ sceneId: sceneId || null,
865
+ reasonCode: 'project_state_scene_capture_failed',
866
+ detail: error instanceof Error ? error.message : String(error),
867
+ hasSnapshot: false,
868
+ snapshot: null,
869
+ };
870
+ }
871
+ }
872
+
873
+ function applySceneSnapshot(scene, snapshot, sceneId = null) {
874
+ if (!scene || snapshot == null) {
875
+ return {
876
+ ok: true,
877
+ sceneId: sceneId || null,
878
+ reasonCode: 'project_state_scene_skipped',
879
+ detail: null,
880
+ restored: false,
881
+ };
882
+ }
883
+ const applyFn = typeof scene.applyState === 'function'
884
+ ? scene.applyState
885
+ : typeof scene.importState === 'function'
886
+ ? scene.importState
887
+ : null;
888
+ if (!applyFn) {
889
+ return {
890
+ ok: true,
891
+ sceneId: sceneId || null,
892
+ reasonCode: 'project_state_scene_skipped',
893
+ detail: null,
894
+ restored: false,
895
+ };
896
+ }
897
+ try {
898
+ const result = applyFn.call(scene, cloneProjectValue(snapshot));
899
+ if (result === false) {
900
+ return {
901
+ ok: false,
902
+ sceneId: sceneId || null,
903
+ reasonCode: 'project_state_scene_apply_failed',
904
+ detail: null,
905
+ restored: false,
906
+ };
907
+ }
908
+ if (result && typeof result === 'object' && result.ok === false) {
909
+ return {
910
+ ok: false,
911
+ sceneId: sceneId || null,
912
+ reasonCode: typeof result.reasonCode === 'string' ? result.reasonCode : 'project_state_scene_apply_failed',
913
+ detail: result.detail || null,
914
+ restored: false,
915
+ };
916
+ }
917
+ return {
918
+ ok: true,
919
+ sceneId: sceneId || null,
920
+ reasonCode: result && typeof result.reasonCode === 'string'
921
+ ? result.reasonCode
922
+ : 'project_state_scene_apply_ok',
923
+ detail: result && typeof result === 'object' ? result.detail || null : null,
924
+ restored: true,
925
+ };
926
+ } catch (error) {
927
+ return {
928
+ ok: false,
929
+ sceneId: sceneId || null,
930
+ reasonCode: 'project_state_scene_apply_failed',
931
+ detail: error instanceof Error ? error.message : String(error),
932
+ restored: false,
933
+ };
934
+ }
935
+ }
936
+
937
+ function normalizeProjectContinuityState(snapshot, fallbackTemplate) {
938
+ if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) {
939
+ return null;
940
+ }
941
+
942
+ const source = snapshot.continuity && typeof snapshot.continuity === 'object' && !Array.isArray(snapshot.continuity)
943
+ ? snapshot.continuity
944
+ : snapshot;
945
+ const sceneStateSource = source.sceneStates && typeof source.sceneStates === 'object' && !Array.isArray(source.sceneStates)
946
+ ? source.sceneStates
947
+ : source.scenes && typeof source.scenes === 'object' && !Array.isArray(source.scenes)
948
+ ? source.scenes
949
+ : {};
950
+ const sceneStates = {};
951
+ for (const sceneId of Object.keys(sceneStateSource).sort()) {
952
+ sceneStates[sceneId] = cloneProjectValue(sceneStateSource[sceneId]);
953
+ }
954
+
955
+ return {
956
+ schema: PROJECT_CONTINUITY_STATE_SCHEMA,
957
+ version: Number.isInteger(Number(source.version)) && Number(source.version) > 0
958
+ ? Number(source.version)
959
+ : PROJECT_CONTINUITY_STATE_VERSION,
960
+ template: typeof source.template === 'string' && source.template.trim()
961
+ ? source.template.trim()
962
+ : fallbackTemplate,
963
+ activeSceneId: typeof source.activeSceneId === 'string' && source.activeSceneId.trim()
964
+ ? source.activeSceneId.trim()
965
+ : null,
966
+ currentSceneData: cloneProjectValue(source.currentSceneData ?? null),
967
+ appState: source.appState && typeof source.appState === 'object' && !Array.isArray(source.appState)
968
+ ? cloneProjectValue(source.appState)
969
+ : null,
970
+ sceneFlow: source.sceneFlow && typeof source.sceneFlow === 'object' && !Array.isArray(source.sceneFlow)
971
+ ? cloneProjectValue(source.sceneFlow)
972
+ : null,
973
+ screenShell: source.screenShell && typeof source.screenShell === 'object' && !Array.isArray(source.screenShell)
974
+ ? cloneProjectValue(source.screenShell)
975
+ : null,
976
+ sceneStates,
977
+ ownership: source.ownership && typeof source.ownership === 'object' && !Array.isArray(source.ownership)
978
+ ? cloneProjectValue(source.ownership)
979
+ : null,
980
+ };
981
+ }
982
+
983
+ function normalizeContinuitySource(source) {
984
+ if (!source || typeof source !== 'object' || Array.isArray(source)) {
985
+ return null;
986
+ }
987
+ return {
988
+ kind: typeof source.kind === 'string' && source.kind.trim() ? source.kind.trim() : null,
989
+ name: typeof source.name === 'string' && source.name.trim() ? source.name.trim() : null,
990
+ path: typeof source.path === 'string' && source.path.trim() ? source.path.trim() : null,
991
+ stateSchemaVersion: typeof source.stateSchemaVersion === 'string' && source.stateSchemaVersion.trim()
992
+ ? source.stateSchemaVersion.trim()
993
+ : null,
994
+ payloadFingerprint: typeof source.payloadFingerprint === 'string' && source.payloadFingerprint.trim()
995
+ ? source.payloadFingerprint.trim()
996
+ : null,
997
+ reasonCode: typeof source.reasonCode === 'string' && source.reasonCode.trim()
998
+ ? source.reasonCode.trim()
999
+ : null,
1000
+ migration: source.migration && typeof source.migration === 'object' && !Array.isArray(source.migration)
1001
+ ? cloneProjectValue(source.migration)
1002
+ : null,
1003
+ };
1004
+ }
1005
+
1006
+ function createContinuitySummary(
1007
+ sceneResults,
1008
+ {
1009
+ phase = 'restore',
1010
+ reasonCode = phase === 'capture' ? 'project_state_capture_ok' : 'project_state_apply_ok',
1011
+ activeSceneId = null,
1012
+ continuityVersion = PROJECT_CONTINUITY_STATE_VERSION,
1013
+ detail = null,
1014
+ source = null,
1015
+ } = {},
1016
+ ) {
1017
+ const normalizedSceneResults = Array.isArray(sceneResults)
1018
+ ? sceneResults.map((entry) => cloneProjectValue(entry))
1019
+ : [];
1020
+ const skippedSceneIds = normalizedSceneResults
1021
+ .filter((entry) => entry?.reasonCode === 'project_state_scene_skipped')
1022
+ .map((entry) => entry.sceneId)
1023
+ .filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
1024
+ const failedSceneIds = normalizedSceneResults
1025
+ .filter((entry) => entry?.ok === false)
1026
+ .map((entry) => entry.sceneId)
1027
+ .filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
1028
+ const capturedSceneIds = normalizedSceneResults
1029
+ .filter((entry) => entry?.hasSnapshot === true)
1030
+ .map((entry) => entry.sceneId)
1031
+ .filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
1032
+ const restoredSceneIds = normalizedSceneResults
1033
+ .filter((entry) => entry?.restored === true)
1034
+ .map((entry) => entry.sceneId)
1035
+ .filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
1036
+ return {
1037
+ phase,
1038
+ reasonCode,
1039
+ activeSceneId: typeof activeSceneId === 'string' && activeSceneId.trim() ? activeSceneId.trim() : null,
1040
+ continuityVersion: Number.isInteger(Number(continuityVersion)) && Number(continuityVersion) > 0
1041
+ ? Number(continuityVersion)
1042
+ : PROJECT_CONTINUITY_STATE_VERSION,
1043
+ totalScenes: normalizedSceneResults.length,
1044
+ capturedSceneIds,
1045
+ restoredSceneIds,
1046
+ skippedSceneIds,
1047
+ failedSceneIds,
1048
+ detail: typeof detail === 'string' && detail.trim() ? detail.trim() : null,
1049
+ source: normalizeContinuitySource(source),
1050
+ sceneResults: normalizedSceneResults,
1051
+ };
1052
+ }
1053
+
1054
+ export function createApp() {
1055
+ const projectTitle = projectRegistry.projectTitle || 'AuraJS Game';
1056
+ const appState = createAppState({
1057
+ projectTitle,
1058
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1059
+ });
1060
+ normalizeAppState(appState, {
1061
+ projectTitle,
1062
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1063
+ });
1064
+ ensureSharedUiPreferences(appState);
1065
+ let sceneRegistry = null;
1066
+ let sceneFlow = null;
1067
+ let screenShell = null;
1068
+ let seededSceneId = null;
1069
+ let runtimeStarted = false;
1070
+ let paused = false;
1071
+ const recordingState = {
1072
+ supported: false,
1073
+ active: false,
1074
+ audioSupported: false,
1075
+ path: null,
1076
+ reasonCode: null,
1077
+ };
1078
+ const currentSceneId = () => sceneFlow?.getState().currentSceneId || seededSceneId || null;
1079
+ function readPendingDevRestoreMeta() {
1080
+ const runtime = globalThis.aura;
1081
+ return normalizeContinuitySource(
1082
+ runtime?.__aurajsPendingDevRestore && typeof runtime.__aurajsPendingDevRestore === 'object'
1083
+ ? runtime.__aurajsPendingDevRestore
1084
+ : runtime?.__aurajsDevRestore,
1085
+ );
1086
+ }
1087
+ function ensureRuntimeContinuityState() {
1088
+ normalizeAppState(appState, {
1089
+ projectTitle,
1090
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1091
+ });
1092
+ const runtimeState = appState.runtime && typeof appState.runtime === 'object' && !Array.isArray(appState.runtime)
1093
+ ? appState.runtime
1094
+ : {};
1095
+ const continuityState = runtimeState.continuity && typeof runtimeState.continuity === 'object' && !Array.isArray(runtimeState.continuity)
1096
+ ? runtimeState.continuity
1097
+ : {
1098
+ lastCapture: null,
1099
+ lastRestore: null,
1100
+ };
1101
+ runtimeState.continuity = continuityState;
1102
+ appState.runtime = runtimeState;
1103
+ return continuityState;
1104
+ }
1105
+ function recordContinuityCapture(summary) {
1106
+ const continuityState = ensureRuntimeContinuityState();
1107
+ continuityState.lastCapture = summary ? cloneProjectValue(summary) : null;
1108
+ return continuityState.lastCapture;
1109
+ }
1110
+ function recordContinuityRestore(summary) {
1111
+ const continuityState = ensureRuntimeContinuityState();
1112
+ continuityState.lastRestore = summary ? cloneProjectValue(summary) : null;
1113
+ return continuityState.lastRestore;
1114
+ }
1115
+ function replaceAppState(nextValue) {
1116
+ if (!nextValue || typeof nextValue !== 'object' || Array.isArray(nextValue)) {
1117
+ return appState;
1118
+ }
1119
+ for (const key of Object.keys(appState)) {
1120
+ delete appState[key];
1121
+ }
1122
+ for (const [key, value] of Object.entries(cloneProjectValue(nextValue))) {
1123
+ appState[key] = value;
1124
+ }
1125
+ normalizeAppState(appState, {
1126
+ projectTitle,
1127
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1128
+ });
1129
+ ensureSharedUiPreferences(appState);
1130
+ paused = appState.runtime?.paused === true;
1131
+ return appState;
1132
+ }
1133
+ function syncAppStateRuntime(previousSceneId = null) {
1134
+ normalizeAppState(appState, {
1135
+ projectTitle,
1136
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1137
+ });
1138
+ const recordingSnapshot = getRecordingState();
1139
+ const runtimeState = appState.runtime && typeof appState.runtime === 'object' && !Array.isArray(appState.runtime)
1140
+ ? appState.runtime
1141
+ : {};
1142
+ const continuityState = runtimeState.continuity && typeof runtimeState.continuity === 'object' && !Array.isArray(runtimeState.continuity)
1143
+ ? cloneProjectValue(runtimeState.continuity)
1144
+ : {
1145
+ lastCapture: null,
1146
+ lastRestore: null,
1147
+ };
1148
+ const shellState = typeof screenShell?.getState === 'function'
1149
+ ? screenShell.getState()
1150
+ : null;
1151
+ appState.projectTitle = projectTitle;
1152
+ appState.template = projectRegistry.template || ${JSON.stringify(template || 'blank')};
1153
+ appState.runtime = {
1154
+ ...runtimeState,
1155
+ startSceneId: projectRegistry.startSceneId || null,
1156
+ currentSceneId: currentSceneId(),
1157
+ previousSceneId: previousSceneId ?? runtimeState.previousSceneId ?? null,
1158
+ hudScreenId: shellState?.hud?.screenId || null,
1159
+ overlayScreenId: shellState?.overlay?.screenId || null,
1160
+ modalScreenIds: Array.isArray(shellState?.modals) ? shellState.modals.map((entry) => entry.screenId) : [],
1161
+ paused,
1162
+ recordingSupported: recordingSnapshot.supported === true,
1163
+ recordingActive: recordingSnapshot.active === true,
1164
+ continuity: continuityState,
1165
+ };
1166
+ }
1167
+ const appContext = {
1168
+ projectTitle,
1169
+ projectRegistry,
1170
+ appState,
1171
+ replaceAppState,
1172
+ getAppState() {
1173
+ return appState;
1174
+ },
1175
+ ensureSessionState(featureKey, seed = {}) {
1176
+ return ensureSessionState(appState, featureKey, seed);
1177
+ },
1178
+ ensureUiState(featureKey, seed = {}) {
1179
+ return ensureUiState(appState, featureKey, seed);
1180
+ },
1181
+ ensureUiPreferences(seed = null) {
1182
+ return ensureSharedUiPreferences(appState, seed);
1183
+ },
1184
+ getSessionState(featureKey, fallback = null) {
1185
+ const key = typeof featureKey === 'string' ? featureKey.trim() : '';
1186
+ if (!key) return fallback;
1187
+ return appState.session?.[key] ?? fallback;
1188
+ },
1189
+ getUiState(featureKey, fallback = null) {
1190
+ const key = typeof featureKey === 'string' ? featureKey.trim() : '';
1191
+ if (!key) return fallback;
1192
+ return appState.ui?.[key] ?? fallback;
1193
+ },
1194
+ getUiPreferences() {
1195
+ return ensureSharedUiPreferences(appState);
1196
+ },
1197
+ updateUiPreferences(patch = {}) {
1198
+ return updateSharedUiPreferences(appState, patch);
1199
+ },
1200
+ resetUiPreferences(seed = null) {
1201
+ return resetSharedUiPreferences(appState, seed);
1202
+ },
1203
+ applyUiTheme(options = {}) {
1204
+ return applySharedUiTheme(appState, options);
1205
+ },
1206
+ getUiThemeState() {
1207
+ return inspectSharedUiTheme(appState);
1208
+ },
1209
+ getActiveSceneId: currentSceneId,
1210
+ getCurrentScenePayload() {
1211
+ return sceneFlow?.current?.()?.data ?? null;
1212
+ },
1213
+ getSceneFlowState() {
1214
+ return sceneFlow?.getState() || null;
1215
+ },
1216
+ getScreenShellState() {
1217
+ return screenShell?.getState() || null;
1218
+ },
1219
+ replaceScene(sceneId, data = null) {
1220
+ const previousSceneId = currentSceneId();
1221
+ const result = sceneFlow?.replace(sceneId, data);
1222
+ if (!result?.ok) {
1223
+ throw new Error(\`Unknown scene "\${sceneId}". [reason:\${result?.reasonCode || 'scene_flow_unavailable'}]\`);
1224
+ }
1225
+ syncAppStateRuntime(previousSceneId);
1226
+ return result;
1227
+ },
1228
+ pushScene(sceneId, data = null) {
1229
+ const previousSceneId = currentSceneId();
1230
+ const result = sceneFlow?.push(sceneId, data);
1231
+ if (!result?.ok) {
1232
+ throw new Error(\`Unable to push scene "\${sceneId}". [reason:\${result?.reasonCode || 'scene_flow_unavailable'}]\`);
1233
+ }
1234
+ syncAppStateRuntime(previousSceneId);
1235
+ return result;
1236
+ },
1237
+ popScene(result = null) {
1238
+ const previousSceneId = currentSceneId();
1239
+ const outcome = sceneFlow?.pop(result);
1240
+ if (!outcome?.ok) {
1241
+ throw new Error(\`Unable to pop scene. [reason:\${outcome?.reasonCode || 'scene_flow_unavailable'}]\`);
1242
+ }
1243
+ syncAppStateRuntime(previousSceneId);
1244
+ return outcome;
1245
+ },
1246
+ canPopScene() {
1247
+ return sceneFlow?.canPop() === true;
1248
+ },
1249
+ setHudScreen(screenId, data = null) {
1250
+ const result = screenShell?.setHud(screenId, data) || null;
1251
+ syncAppStateRuntime(currentSceneId());
1252
+ return result;
1253
+ },
1254
+ clearHudScreen() {
1255
+ const result = screenShell?.clearHud() || null;
1256
+ syncAppStateRuntime(currentSceneId());
1257
+ return result;
1258
+ },
1259
+ showOverlayScreen(screenId, data = null) {
1260
+ const result = screenShell?.showOverlay(screenId, data) || null;
1261
+ syncAppStateRuntime(currentSceneId());
1262
+ return result;
1263
+ },
1264
+ clearOverlayScreen() {
1265
+ const result = screenShell?.clearOverlay() || null;
1266
+ syncAppStateRuntime(currentSceneId());
1267
+ return result;
1268
+ },
1269
+ pushModalScreen(screenId, data = null) {
1270
+ const result = screenShell?.pushModal(screenId, data) || null;
1271
+ syncAppStateRuntime(currentSceneId());
1272
+ return result;
1273
+ },
1274
+ popModalScreen(result = null) {
1275
+ const outcome = screenShell?.popModal(result) || null;
1276
+ syncAppStateRuntime(currentSceneId());
1277
+ return outcome;
1278
+ },
1279
+ jumpToScene(sceneId, data = null) {
1280
+ return jumpToScene(sceneId, data);
1281
+ },
1282
+ swapScene(sceneId, data = null) {
1283
+ return this.replaceScene(sceneId, data);
1284
+ },
1285
+ setPaused(nextValue = true) {
1286
+ return setPaused(nextValue);
1287
+ },
1288
+ togglePaused() {
1289
+ return togglePaused();
1290
+ },
1291
+ restartCurrentScene() {
1292
+ return restartCurrentScene();
1293
+ },
1294
+ restartStartScene() {
1295
+ return restartStartScene();
1296
+ },
1297
+ get sceneRegistry() {
1298
+ return sceneRegistry;
1299
+ },
1300
+ get sceneFlow() {
1301
+ return sceneFlow;
1302
+ },
1303
+ get screenShell() {
1304
+ return screenShell;
1305
+ },
1306
+ };
1307
+ sceneRegistry = createSceneRegistry(appContext);
1308
+ seededSceneId = sceneRegistry.defaultSceneId || Object.keys(sceneRegistry.scenes || {})[0] || null;
1309
+ const resolveScene = (sceneId) => sceneRegistry.scenes[sceneId] || null;
1310
+ screenShell = createScreenShell({ resolveScreen: resolveProjectScreen });
1311
+ sceneFlow = createSceneFlow({
1312
+ initialSceneId: seededSceneId,
1313
+ resolveScene,
1314
+ onEnter(info) {
1315
+ if (!runtimeStarted) return;
1316
+ info.current?.scene?.setup?.(info.current.data);
1317
+ info.current?.scene?.onEnter?.(info);
1318
+ },
1319
+ onExit(info) {
1320
+ info.current?.scene?.onExit?.(info);
1321
+ info.current?.scene?.shutdown?.(info);
1322
+ },
1323
+ onPause(info) {
1324
+ info.current?.scene?.onPause?.(info);
1325
+ },
1326
+ onResume(info) {
1327
+ info.current?.scene?.onResume?.(info);
1328
+ },
1329
+ });
1330
+ syncAppStateRuntime();
1331
+ const activeScene = () => {
1332
+ const sceneId = currentSceneId();
1333
+ return sceneId ? sceneRegistry.scenes[sceneId] || null : null;
1334
+ };
1335
+ function clearPlaytestScreens() {
1336
+ const shellState = typeof screenShell?.getState === 'function' ? screenShell.getState() : null;
1337
+ if (!shellState || typeof screenShell?.applyState !== 'function') {
1338
+ return null;
1339
+ }
1340
+ return screenShell.applyState({
1341
+ schema: shellState.schema || 'aurajs.screen-shell.v1',
1342
+ hud: shellState.hud || null,
1343
+ overlay: null,
1344
+ modals: [],
1345
+ mutationCount: Number.isInteger(Number(shellState.mutationCount))
1346
+ ? Number(shellState.mutationCount) + 1
1347
+ : 1,
1348
+ });
1349
+ }
1350
+ function setPaused(nextValue = false) {
1351
+ paused = nextValue === true;
1352
+ syncAppStateRuntime();
1353
+ return paused;
1354
+ }
1355
+ function togglePaused() {
1356
+ return setPaused(!paused);
1357
+ }
1358
+ function getPaused() {
1359
+ return paused === true;
1360
+ }
1361
+ function getRecordingState() {
1362
+ const runtime = globalThis.aura;
1363
+ const state = runtime?.window && typeof runtime.window.getRecordingState === 'function'
1364
+ ? runtime.window.getRecordingState()
1365
+ : null;
1366
+ recordingState.supported = state?.supported === true;
1367
+ recordingState.active = state?.active === true;
1368
+ recordingState.audioSupported = state?.audioSupported === true;
1369
+ recordingState.path = typeof state?.path === 'string' ? state.path : null;
1370
+ recordingState.reasonCode = typeof state?.reasonCode === 'string' ? state.reasonCode : null;
1371
+ return {
1372
+ supported: recordingState.supported === true,
1373
+ active: recordingState.active === true,
1374
+ audioSupported: recordingState.audioSupported === true,
1375
+ path: recordingState.path || null,
1376
+ reasonCode: recordingState.reasonCode || null,
1377
+ };
1378
+ }
1379
+ function toggleRecording() {
1380
+ const runtime = globalThis.aura;
1381
+ if (!runtime?.window || typeof runtime.window.toggleRecording !== 'function') {
1382
+ return {
1383
+ ok: false,
1384
+ reasonCode: 'recording_unavailable',
1385
+ };
1386
+ }
1387
+ runtime.window.toggleRecording();
1388
+ return {
1389
+ ok: true,
1390
+ reasonCode: 'recording_toggle_requested',
1391
+ };
1392
+ }
1393
+ function restartCurrentScene() {
1394
+ const current = sceneFlow?.current?.();
1395
+ if (!current?.sceneId) {
1396
+ return {
1397
+ ok: false,
1398
+ reasonCode: 'scene_flow_unavailable',
1399
+ };
1400
+ }
1401
+ clearPlaytestScreens();
1402
+ paused = false;
1403
+ const result = sceneFlow.replace(current.sceneId, cloneProjectValue(current.data));
1404
+ if (result?.ok) {
1405
+ syncAppStateRuntime(current.sceneId);
1406
+ }
1407
+ return result;
1408
+ }
1409
+ function jumpToScene(sceneId, data = null) {
1410
+ const targetSceneId = typeof sceneId === 'string' ? sceneId.trim() : '';
1411
+ if (!targetSceneId) {
1412
+ return {
1413
+ ok: false,
1414
+ reasonCode: 'scene_flow_invalid_scene_id',
1415
+ };
1416
+ }
1417
+ if (!sceneRegistry?.scenes?.[targetSceneId]) {
1418
+ return {
1419
+ ok: false,
1420
+ reasonCode: 'scene_flow_unknown_scene',
1421
+ };
1422
+ }
1423
+ const previousSceneId = currentSceneId();
1424
+ clearPlaytestScreens();
1425
+ while (sceneFlow?.canPop?.()) {
1426
+ const popped = sceneFlow.pop();
1427
+ if (!popped?.ok) {
1428
+ syncAppStateRuntime(previousSceneId);
1429
+ return popped;
1430
+ }
1431
+ }
1432
+ paused = false;
1433
+ const result = sceneFlow.replace(targetSceneId, cloneProjectValue(data));
1434
+ if (result?.ok) {
1435
+ syncAppStateRuntime(previousSceneId);
1436
+ }
1437
+ return result;
1438
+ }
1439
+ function restartStartScene() {
1440
+ const startSceneId = projectRegistry.startSceneId || seededSceneId || currentSceneId();
1441
+ if (!startSceneId) {
1442
+ return {
1443
+ ok: false,
1444
+ reasonCode: 'scene_flow_unavailable',
1445
+ };
1446
+ }
1447
+ return jumpToScene(startSceneId, null);
1448
+ }
1449
+ function exportProjectState() {
1450
+ const scenes = {};
1451
+ const sceneCaptureResults = [];
1452
+ for (const sceneId of Object.keys(sceneRegistry.scenes || {}).sort()) {
1453
+ const captureResult = captureSceneSnapshot(sceneRegistry.scenes[sceneId], sceneId);
1454
+ sceneCaptureResults.push({
1455
+ sceneId,
1456
+ ok: captureResult.ok === true,
1457
+ reasonCode: captureResult.reasonCode || null,
1458
+ detail: captureResult.detail || null,
1459
+ hasSnapshot: captureResult.hasSnapshot === true,
1460
+ });
1461
+ if (captureResult.snapshot != null) {
1462
+ scenes[sceneId] = captureResult.snapshot;
1463
+ }
1464
+ }
1465
+ const captureReasonCode = sceneCaptureResults.some((entry) => entry.ok !== true || entry.reasonCode === 'project_state_scene_skipped')
1466
+ ? 'project_state_capture_partial'
1467
+ : 'project_state_capture_ok';
1468
+ recordContinuityCapture(createContinuitySummary(sceneCaptureResults, {
1469
+ phase: 'capture',
1470
+ reasonCode: captureReasonCode,
1471
+ activeSceneId: currentSceneId(),
1472
+ }));
1473
+ syncAppStateRuntime();
1474
+ const continuity = normalizeProjectContinuityState({
1475
+ schema: PROJECT_CONTINUITY_STATE_SCHEMA,
1476
+ version: PROJECT_CONTINUITY_STATE_VERSION,
1477
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1478
+ activeSceneId: currentSceneId(),
1479
+ currentSceneData: sceneFlow?.current?.()?.data ?? null,
1480
+ appState,
1481
+ sceneFlow: sceneFlow.getState(),
1482
+ screenShell: screenShell.getState(),
1483
+ sceneStates: scenes,
1484
+ ownership: projectRegistry.continuity || null,
1485
+ }, projectRegistry.template || ${JSON.stringify(template || 'blank')});
1486
+ return {
1487
+ schema: PROJECT_STATE_SCHEMA,
1488
+ template: continuity.template,
1489
+ activeSceneId: continuity.activeSceneId,
1490
+ currentSceneData: continuity.currentSceneData,
1491
+ appState: continuity.appState,
1492
+ sceneFlow: continuity.sceneFlow,
1493
+ screenShell: continuity.screenShell,
1494
+ scenes: continuity.sceneStates,
1495
+ continuity,
1496
+ };
1497
+ }
1498
+ function applyProjectState(snapshot) {
1499
+ if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) {
1500
+ return { ok: false, reasonCode: 'project_state_invalid' };
1501
+ }
1502
+ if (snapshot.schema && snapshot.schema !== PROJECT_STATE_SCHEMA) {
1503
+ return { ok: false, reasonCode: 'project_state_invalid' };
1504
+ }
1505
+ const continuity = normalizeProjectContinuityState(
1506
+ snapshot,
1507
+ projectRegistry.template || ${JSON.stringify(template || 'blank')},
1508
+ );
1509
+ if (!continuity) {
1510
+ return { ok: false, reasonCode: 'project_state_invalid' };
1511
+ }
1512
+
1513
+ const beforeSceneId = currentSceneId();
1514
+ const restoreSource = readPendingDevRestoreMeta();
1515
+ if (continuity.appState) {
1516
+ replaceAppState(continuity.appState);
1517
+ }
1518
+ const flowResult = sceneFlow.applyState(continuity.sceneFlow || null);
1519
+ if (!flowResult?.ok) {
1520
+ const continuitySummary = createContinuitySummary([], {
1521
+ phase: 'restore',
1522
+ reasonCode: flowResult?.reasonCode || 'project_state_apply_failed',
1523
+ activeSceneId: beforeSceneId,
1524
+ continuityVersion: continuity.version,
1525
+ detail: flowResult?.detail || null,
1526
+ source: restoreSource,
1527
+ });
1528
+ recordContinuityRestore(continuitySummary);
1529
+ syncAppStateRuntime(beforeSceneId);
1530
+ return {
1531
+ ok: false,
1532
+ reasonCode: flowResult?.reasonCode || 'project_state_apply_failed',
1533
+ detail: flowResult?.detail || null,
1534
+ continuity: continuitySummary,
1535
+ };
1536
+ }
1537
+
1538
+ const shellResult = screenShell.applyState(continuity.screenShell || {
1539
+ schema: 'aurajs.screen-shell.v1',
1540
+ hud: null,
1541
+ overlay: null,
1542
+ modals: [],
1543
+ mutationCount: 0,
1544
+ });
1545
+ if (!shellResult?.ok) {
1546
+ const continuitySummary = createContinuitySummary([], {
1547
+ phase: 'restore',
1548
+ reasonCode: shellResult?.reasonCode || 'project_state_apply_failed',
1549
+ activeSceneId: currentSceneId(),
1550
+ continuityVersion: continuity.version,
1551
+ detail: shellResult?.detail || null,
1552
+ source: restoreSource,
1553
+ });
1554
+ recordContinuityRestore(continuitySummary);
1555
+ syncAppStateRuntime(beforeSceneId);
1556
+ return {
1557
+ ok: false,
1558
+ reasonCode: shellResult?.reasonCode || 'project_state_apply_failed',
1559
+ detail: shellResult?.detail || null,
1560
+ continuity: continuitySummary,
1561
+ };
1562
+ }
1563
+
1564
+ const sceneSnapshots = continuity.sceneStates;
1565
+ const sceneRestoreResults = [];
1566
+ for (const [sceneId, sceneSnapshot] of Object.entries(sceneSnapshots)) {
1567
+ const result = applySceneSnapshot(sceneRegistry.scenes[sceneId] || null, sceneSnapshot, sceneId);
1568
+ sceneRestoreResults.push({
1569
+ sceneId,
1570
+ ok: result.ok === true,
1571
+ reasonCode: result.reasonCode || null,
1572
+ detail: result.detail || null,
1573
+ restored: result.restored === true,
1574
+ });
1575
+ if (!result.ok) {
1576
+ const continuitySummary = createContinuitySummary(sceneRestoreResults, {
1577
+ phase: 'restore',
1578
+ reasonCode: result.reasonCode || 'project_state_scene_apply_failed',
1579
+ activeSceneId: currentSceneId(),
1580
+ continuityVersion: continuity.version,
1581
+ detail: result.detail || null,
1582
+ source: restoreSource,
1583
+ });
1584
+ recordContinuityRestore(continuitySummary);
1585
+ syncAppStateRuntime(beforeSceneId);
1586
+ return {
1587
+ ok: false,
1588
+ reasonCode: result.reasonCode || 'project_state_scene_apply_failed',
1589
+ failedSceneId: sceneId,
1590
+ detail: result.detail || null,
1591
+ continuity: continuitySummary,
1592
+ };
1593
+ }
1594
+ }
1595
+
1596
+ const afterSceneId = currentSceneId();
1597
+ if (runtimeStarted && afterSceneId && afterSceneId !== beforeSceneId) {
1598
+ const scene = activeScene();
1599
+ scene?.setup?.(sceneFlow.current()?.data);
1600
+ }
1601
+ const restoreReasonCode = sceneRestoreResults.some((entry) => entry.reasonCode === 'project_state_scene_skipped')
1602
+ ? 'project_state_apply_partial'
1603
+ : 'project_state_apply_ok';
1604
+ const continuitySummary = createContinuitySummary(sceneRestoreResults, {
1605
+ phase: 'restore',
1606
+ reasonCode: restoreReasonCode,
1607
+ activeSceneId: afterSceneId || null,
1608
+ continuityVersion: continuity.version,
1609
+ source: restoreSource,
1610
+ });
1611
+ recordContinuityRestore(continuitySummary);
1612
+ syncAppStateRuntime(beforeSceneId);
1613
+
1614
+ return {
1615
+ ok: true,
1616
+ reasonCode: restoreReasonCode,
1617
+ activeSceneId: afterSceneId || null,
1618
+ continuityVersion: continuity.version,
1619
+ continuity: continuitySummary,
1620
+ };
1621
+ }
1622
+ function installProjectStateBridge(app) {
1623
+ const auraRef = globalThis.aura;
1624
+ if (!auraRef || typeof auraRef !== 'object') {
1625
+ return;
1626
+ }
1627
+ auraRef.project = {
1628
+ schemaVersion: PROJECT_STATE_SCHEMA,
1629
+ continuitySchemaVersion: PROJECT_CONTINUITY_STATE_SCHEMA,
1630
+ getState: () => app.getState(),
1631
+ exportState: () => app.getState(),
1632
+ getContinuityState: () => app.appState?.runtime?.continuity || null,
1633
+ applyState: (snapshot) => app.applyState(snapshot),
1634
+ apply: (snapshot) => app.applyState(snapshot),
1635
+ };
1636
+ }
1637
+ const projectInspector = createProjectInspector({
1638
+ projectTitle,
1639
+ sceneRegistry,
1640
+ getActiveSceneId: currentSceneId,
1641
+ appState,
1642
+ getUiThemeState: () => inspectSharedUiTheme(appState),
1643
+ onSelectScene: jumpToScene,
1644
+ onRestartScene: restartCurrentScene,
1645
+ onRestartApp: restartStartScene,
1646
+ getPaused,
1647
+ togglePaused,
1648
+ getRecordingState,
1649
+ toggleRecording,
1650
+ });
1651
+
1652
+ function renderScreenEntry(entry) {
1653
+ if (!entry) return;
1654
+ const screen = resolveProjectScreen(entry.screenId);
1655
+ if (typeof screen === 'function') {
1656
+ screen(entry.data || {}, appContext);
1657
+ return;
1658
+ }
1659
+ const drawScreen = typeof screen?.draw === 'function'
1660
+ ? screen.draw
1661
+ : typeof screen?.render === 'function'
1662
+ ? screen.render
1663
+ : null;
1664
+ if (drawScreen) {
1665
+ drawScreen(entry.data || {}, appContext);
1666
+ }
1667
+ }
1668
+
1669
+ const app = {
1670
+ ...appContext,
1671
+ template: projectRegistry.template || ${JSON.stringify(template || 'blank')},
1672
+ projectInspector,
1673
+ get projectInspectorEnabled() {
1674
+ return projectInspector.enabled;
1675
+ },
1676
+ get projectInspectorMinimized() {
1677
+ return projectInspector.minimized;
1678
+ },
1679
+ get paused() {
1680
+ return paused;
1681
+ },
1682
+ get activeSceneId() {
1683
+ return currentSceneId();
1684
+ },
1685
+ get sceneRegistry() {
1686
+ return sceneRegistry;
1687
+ },
1688
+ get sceneFlow() {
1689
+ return sceneFlow;
1690
+ },
1691
+ get screenShell() {
1692
+ return screenShell;
1693
+ },
1694
+ inspectProject() {
1695
+ return projectInspector.snapshot();
1696
+ },
1697
+ get appState() {
1698
+ return appState;
1699
+ },
1700
+ getProjectInspectorState() {
1701
+ return projectInspector.snapshot();
1702
+ },
1703
+ getSceneFlowState() {
1704
+ return sceneFlow.getState();
1705
+ },
1706
+ getScreenShellState() {
1707
+ return screenShell.getState();
1708
+ },
1709
+ getState() {
1710
+ return exportProjectState();
1711
+ },
1712
+ exportState() {
1713
+ return exportProjectState();
1714
+ },
1715
+ applyState(snapshot) {
1716
+ return applyProjectState(snapshot);
1717
+ },
1718
+ replaceScene(sceneId, data = null) {
1719
+ const previousSceneId = currentSceneId();
1720
+ const result = sceneFlow.replace(sceneId, data);
1721
+ if (!result.ok) {
1722
+ throw new Error(\`Unknown scene "\${sceneId}". [reason:\${result.reasonCode}]\`);
1723
+ }
1724
+ syncAppStateRuntime(previousSceneId);
1725
+ return result;
1726
+ },
1727
+ pushScene(sceneId, data = null) {
1728
+ const previousSceneId = currentSceneId();
1729
+ const result = sceneFlow.push(sceneId, data);
1730
+ if (!result.ok) {
1731
+ throw new Error(\`Unable to push scene "\${sceneId}". [reason:\${result.reasonCode}]\`);
1732
+ }
1733
+ syncAppStateRuntime(previousSceneId);
1734
+ return result;
1735
+ },
1736
+ popScene(result = null) {
1737
+ const previousSceneId = currentSceneId();
1738
+ const outcome = sceneFlow.pop(result);
1739
+ if (!outcome.ok) {
1740
+ throw new Error(\`Unable to pop scene. [reason:\${outcome.reasonCode}]\`);
1741
+ }
1742
+ syncAppStateRuntime(previousSceneId);
1743
+ return outcome;
1744
+ },
1745
+ jumpToScene(sceneId, data = null) {
1746
+ return jumpToScene(sceneId, data);
1747
+ },
1748
+ canPopScene() {
1749
+ return sceneFlow.canPop();
1750
+ },
1751
+ setHudScreen(screenId, data = null) {
1752
+ const result = screenShell.setHud(screenId, data);
1753
+ syncAppStateRuntime(currentSceneId());
1754
+ return result;
1755
+ },
1756
+ clearHudScreen() {
1757
+ const result = screenShell.clearHud();
1758
+ syncAppStateRuntime(currentSceneId());
1759
+ return result;
1760
+ },
1761
+ showOverlayScreen(screenId, data = null) {
1762
+ const result = screenShell.showOverlay(screenId, data);
1763
+ syncAppStateRuntime(currentSceneId());
1764
+ return result;
1765
+ },
1766
+ clearOverlayScreen() {
1767
+ const result = screenShell.clearOverlay();
1768
+ syncAppStateRuntime(currentSceneId());
1769
+ return result;
1770
+ },
1771
+ pushModalScreen(screenId, data = null) {
1772
+ const result = screenShell.pushModal(screenId, data);
1773
+ syncAppStateRuntime(currentSceneId());
1774
+ return result;
1775
+ },
1776
+ popModalScreen(result = null) {
1777
+ const outcome = screenShell.popModal(result);
1778
+ syncAppStateRuntime(currentSceneId());
1779
+ return outcome;
1780
+ },
1781
+ toggleProjectInspector(nextValue = null) {
1782
+ if (typeof nextValue === 'boolean') {
1783
+ return projectInspector.setEnabled(nextValue);
1784
+ }
1785
+ return projectInspector.toggle();
1786
+ },
1787
+ setProjectInspectorMinimized(nextValue = null) {
1788
+ if (typeof nextValue === 'boolean') {
1789
+ return projectInspector.setMinimized(nextValue);
1790
+ }
1791
+ return projectInspector.setMinimized(false);
1792
+ },
1793
+ toggleProjectInspectorMinimized() {
1794
+ return projectInspector.toggleMinimized();
1795
+ },
1796
+ setPaused(nextValue = true) {
1797
+ return setPaused(nextValue);
1798
+ },
1799
+ togglePaused() {
1800
+ return togglePaused();
1801
+ },
1802
+ getRecordingState() {
1803
+ return getRecordingState();
1804
+ },
1805
+ toggleRecording() {
1806
+ return toggleRecording();
1807
+ },
1808
+ restartCurrentScene() {
1809
+ return restartCurrentScene();
1810
+ },
1811
+ restartStartScene() {
1812
+ return restartStartScene();
1813
+ },
1814
+ setup() {
1815
+ assertRuntimeCapabilities();
1816
+ runtimeStarted = true;
1817
+ paused = false;
1818
+ activeScene()?.setup?.();
1819
+ syncAppStateRuntime();
1820
+ },
1821
+ update(dt) {
1822
+ projectInspector.update();
1823
+ if (!paused) {
1824
+ activeScene()?.update?.(dt);
1825
+ }
1826
+ syncAppStateRuntime();
1827
+ },
1828
+ draw() {
1829
+ applySharedUiTheme(appState);
1830
+ activeScene()?.draw?.();
1831
+ const shellState = screenShell.getState();
1832
+ renderScreenEntry(shellState.hud);
1833
+ renderScreenEntry(shellState.overlay);
1834
+ for (const modal of shellState.modals) {
1835
+ renderScreenEntry(modal);
1836
+ }
1837
+ projectInspector.draw();
1838
+ },
1839
+ swapScene(nextSceneId, data = null) {
1840
+ return appContext.replaceScene(nextSceneId, data);
1841
+ },
1842
+ };
1843
+ installProjectStateBridge(app);
1844
+ return app;
1845
+ }
1846
+ `;
1847
+ }
1848
+
1849
+ export function renderRegistryBackedMainEntry({ projectTitle, starterLabel = null }) {
1850
+ const bootstrapLabel = starterLabel
1851
+ ? `AuraJS ${starterLabel} starter`
1852
+ : 'AuraJS authored project';
1853
+ return `// ${projectTitle} - ${bootstrapLabel} bootstrap
1854
+
1855
+ import { createApp } from './runtime/app.js';
1856
+
1857
+ const app = createApp();
1858
+
1859
+ aura.setup = function () {
1860
+ app.setup();
1861
+ };
1862
+
1863
+ aura.update = function (dt) {
1864
+ app.update(dt);
1865
+ };
1866
+
1867
+ aura.draw = function () {
1868
+ app.draw();
1869
+ };
1870
+ `;
1871
+ }
1872
+
1873
+ export function renderBlankRuntimeCapabilitiesModule() {
1874
+ return `function hasMethod(obj, method) {
1875
+ return Boolean(obj) && typeof obj[method] === 'function';
1876
+ }
1877
+
1878
+ export function assertRuntimeCapabilities() {
1879
+ const missing = [];
1880
+ if (!hasMethod(aura.draw2d, 'clear')) missing.push('aura.draw2d.clear');
1881
+ if (!hasMethod(aura.draw2d, 'rect')) missing.push('aura.draw2d.rect');
1882
+ if (typeof aura.rgba !== 'function') missing.push('aura.rgba');
1883
+
1884
+ if (missing.length > 0) {
1885
+ throw new Error(\`[authored-project] runtime missing required APIs: \${missing.join(', ')} [reason:missing_runtime_api]\`);
1886
+ }
1887
+ }
1888
+ `;
1889
+ }
1890
+
1891
+ export function renderBlankBootstrapEntry({ projectTitle }) {
1892
+ return `// ${projectTitle} — AuraJS blank starter
1893
+
1894
+ function hasMethod(obj, method) {
1895
+ return Boolean(obj) && typeof obj[method] === 'function';
1896
+ }
1897
+
1898
+ function assertRuntimeCapabilities() {
1899
+ const missing = [];
1900
+ if (!hasMethod(aura.draw2d, 'clear')) missing.push('aura.draw2d.clear');
1901
+ if (typeof aura.rgba !== 'function') missing.push('aura.rgba');
1902
+
1903
+ if (missing.length > 0) {
1904
+ throw new Error(\`[blank-template] runtime missing required APIs: \${missing.join(', ')} [reason:missing_runtime_api]\`);
1905
+ }
1906
+ }
1907
+
1908
+ aura.setup = function () {
1909
+ assertRuntimeCapabilities();
1910
+ console.log(${JSON.stringify(`${projectTitle} ready`)});
1911
+ };
1912
+
1913
+ aura.update = function (_dt) {
1914
+ // game logic
1915
+ };
1916
+
1917
+ aura.draw = function () {
1918
+ aura.draw2d.clear(aura.rgba(0.06, 0.06, 0.08, 1.0));
1919
+ };
1920
+ `;
1921
+ }
1922
+
1923
+ export function syncRegistryBackedRuntimeFiles({ projectRoot, template, projectTitle }) {
1924
+ const appPath = resolve(projectRoot, PROJECT_RUNTIME_APP_RELATIVE_PATH);
1925
+ const appStatePath = resolve(projectRoot, PROJECT_RUNTIME_APP_STATE_RELATIVE_PATH);
1926
+ const capabilitiesPath = resolve(projectRoot, PROJECT_RUNTIME_CAPABILITIES_RELATIVE_PATH);
1927
+ const inspectorPath = resolve(projectRoot, PROJECT_RUNTIME_INSPECTOR_RELATIVE_PATH);
1928
+ const sceneFlowPath = resolve(projectRoot, PROJECT_RUNTIME_SCENE_FLOW_RELATIVE_PATH);
1929
+ const screenShellPath = resolve(projectRoot, PROJECT_RUNTIME_SCREEN_SHELL_RELATIVE_PATH);
1930
+ const uiThemePath = resolve(projectRoot, PROJECT_RUNTIME_UI_THEME_RELATIVE_PATH);
1931
+ const uiSettingsPath = resolve(projectRoot, PROJECT_RUNTIME_UI_SETTINGS_RELATIVE_PATH);
1932
+ const uiFormsPath = resolve(projectRoot, PROJECT_RUNTIME_UI_FORMS_RELATIVE_PATH);
1933
+ const mainPath = resolve(projectRoot, PROJECT_MAIN_ENTRY_RELATIVE_PATH);
1934
+ const written = [];
1935
+ const appStateModule = renderAppStateModule();
1936
+ const inspectorModule = renderProjectInspectorModule();
1937
+ const sceneFlowModule = renderSceneFlowModule();
1938
+ const screenShellModule = renderScreenShellModule();
1939
+ const uiThemeModule = renderUiThemeModule();
1940
+ const uiSettingsModule = renderUiSettingsModule();
1941
+ const uiFormsModule = renderUiFormsModule();
1942
+
1943
+ if (!existsSync(appStatePath) || readFileSync(appStatePath, 'utf8') !== appStateModule) {
1944
+ mkdirSync(dirname(appStatePath), { recursive: true });
1945
+ writeFileSync(appStatePath, appStateModule, 'utf8');
1946
+ written.push(PROJECT_RUNTIME_APP_STATE_RELATIVE_PATH);
1947
+ }
1948
+
1949
+ if (!existsSync(inspectorPath) || readFileSync(inspectorPath, 'utf8') !== inspectorModule) {
1950
+ mkdirSync(dirname(inspectorPath), { recursive: true });
1951
+ writeFileSync(inspectorPath, inspectorModule, 'utf8');
1952
+ written.push(PROJECT_RUNTIME_INSPECTOR_RELATIVE_PATH);
1953
+ }
1954
+
1955
+ if (!existsSync(screenShellPath) || readFileSync(screenShellPath, 'utf8') !== screenShellModule) {
1956
+ mkdirSync(dirname(screenShellPath), { recursive: true });
1957
+ writeFileSync(screenShellPath, screenShellModule, 'utf8');
1958
+ written.push(PROJECT_RUNTIME_SCREEN_SHELL_RELATIVE_PATH);
1959
+ }
1960
+
1961
+ if (!existsSync(uiThemePath) || readFileSync(uiThemePath, 'utf8') !== uiThemeModule) {
1962
+ mkdirSync(dirname(uiThemePath), { recursive: true });
1963
+ writeFileSync(uiThemePath, uiThemeModule, 'utf8');
1964
+ written.push(PROJECT_RUNTIME_UI_THEME_RELATIVE_PATH);
1965
+ }
1966
+
1967
+ if (!existsSync(uiSettingsPath) || readFileSync(uiSettingsPath, 'utf8') !== uiSettingsModule) {
1968
+ mkdirSync(dirname(uiSettingsPath), { recursive: true });
1969
+ writeFileSync(uiSettingsPath, uiSettingsModule, 'utf8');
1970
+ written.push(PROJECT_RUNTIME_UI_SETTINGS_RELATIVE_PATH);
1971
+ }
1972
+
1973
+ if (!existsSync(uiFormsPath) || readFileSync(uiFormsPath, 'utf8') !== uiFormsModule) {
1974
+ mkdirSync(dirname(uiFormsPath), { recursive: true });
1975
+ writeFileSync(uiFormsPath, uiFormsModule, 'utf8');
1976
+ written.push(PROJECT_RUNTIME_UI_FORMS_RELATIVE_PATH);
1977
+ }
1978
+
1979
+ if (!existsSync(sceneFlowPath) || readFileSync(sceneFlowPath, 'utf8') !== sceneFlowModule) {
1980
+ mkdirSync(dirname(sceneFlowPath), { recursive: true });
1981
+ writeFileSync(sceneFlowPath, sceneFlowModule, 'utf8');
1982
+ written.push(PROJECT_RUNTIME_SCENE_FLOW_RELATIVE_PATH);
1983
+ }
1984
+
1985
+ if (!existsSync(appPath)) {
1986
+ mkdirSync(dirname(appPath), { recursive: true });
1987
+ writeFileSync(appPath, renderRegistryBackedRuntimeApp({ template }), 'utf8');
1988
+ written.push(PROJECT_RUNTIME_APP_RELATIVE_PATH);
1989
+ }
1990
+
1991
+ if (String(template || '').trim() === 'blank' && !existsSync(capabilitiesPath)) {
1992
+ mkdirSync(dirname(capabilitiesPath), { recursive: true });
1993
+ writeFileSync(capabilitiesPath, renderBlankRuntimeCapabilitiesModule(), 'utf8');
1994
+ written.push(PROJECT_RUNTIME_CAPABILITIES_RELATIVE_PATH);
1995
+ }
1996
+
1997
+ const blankBootstrap = renderBlankBootstrapEntry({ projectTitle });
1998
+ const existingMainSource = existsSync(mainPath) ? readFileSync(mainPath, 'utf8') : null;
1999
+ const shouldReplaceBlankMain = !existsSync(mainPath)
2000
+ || existingMainSource === blankBootstrap
2001
+ || (
2002
+ typeof existingMainSource === 'string'
2003
+ && existingMainSource.includes('AuraJS blank starter')
2004
+ && !existingMainSource.includes('createApp')
2005
+ );
2006
+ if (String(template || '').trim() === 'blank' && shouldReplaceBlankMain) {
2007
+ mkdirSync(dirname(mainPath), { recursive: true });
2008
+ writeFileSync(mainPath, renderRegistryBackedMainEntry({ projectTitle }), 'utf8');
2009
+ written.push(PROJECT_MAIN_ENTRY_RELATIVE_PATH);
2010
+ }
2011
+
2012
+ return {
2013
+ files: written,
2014
+ mainAdopted: written.includes(PROJECT_MAIN_ENTRY_RELATIVE_PATH),
2015
+ };
2016
+ }