lecodes-sdk 0.19.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 (367) hide show
  1. package/README.md +114 -0
  2. package/dist/global.d.ts +503 -0
  3. package/dist/host.d.ts +61 -0
  4. package/dist/inject.js +4730 -0
  5. package/dist/types/animate/animate.d.ts +20 -0
  6. package/dist/types/animate/bezier.d.ts +2 -0
  7. package/dist/types/animate/easings.d.ts +21 -0
  8. package/dist/types/canvas/Canvas.d.ts +168 -0
  9. package/dist/types/core/Aspect.d.ts +171 -0
  10. package/dist/types/core/InspectorUI.d.ts +88 -0
  11. package/dist/types/core/StateMachine.d.ts +81 -0
  12. package/dist/types/core/color.d.ts +13 -0
  13. package/dist/types/core/compWrite.d.ts +11 -0
  14. package/dist/types/core/events.d.ts +11 -0
  15. package/dist/types/core/fields.d.ts +58 -0
  16. package/dist/types/core/registry.d.ts +7 -0
  17. package/dist/types/core/signals.d.ts +29 -0
  18. package/dist/types/core/time.d.ts +31 -0
  19. package/dist/types/g2/Camera2D.d.ts +19 -0
  20. package/dist/types/g2/CharacterController2D.d.ts +134 -0
  21. package/dist/types/g2/Node2D.d.ts +91 -0
  22. package/dist/types/g2/OneWay2D.d.ts +24 -0
  23. package/dist/types/g2/Physics2D.d.ts +147 -0
  24. package/dist/types/g2/Scene2D.d.ts +72 -0
  25. package/dist/types/g2/Shape2D.d.ts +58 -0
  26. package/dist/types/g2/Sprite.d.ts +51 -0
  27. package/dist/types/g2/SpriteAnimation.d.ts +50 -0
  28. package/dist/types/g2/SpriteSheet.d.ts +69 -0
  29. package/dist/types/g2/Texture2D.d.ts +16 -0
  30. package/dist/types/g2/Tilemap.d.ts +20 -0
  31. package/dist/types/g2/Tileset.d.ts +32 -0
  32. package/dist/types/g2/Trigger2D.d.ts +20 -0
  33. package/dist/types/g2/autotile.d.ts +133 -0
  34. package/dist/types/g2/cells.d.ts +14 -0
  35. package/dist/types/g2/defineScene2d.d.ts +141 -0
  36. package/dist/types/g2/groups2d.d.ts +16 -0
  37. package/dist/types/g2/loop.d.ts +4 -0
  38. package/dist/types/g2/scenarios2d.d.ts +30 -0
  39. package/dist/types/g2/touch.d.ts +2 -0
  40. package/dist/types/gl/Camera.d.ts +79 -0
  41. package/dist/types/gl/CameraPlace.d.ts +26 -0
  42. package/dist/types/gl/CharacterController.d.ts +98 -0
  43. package/dist/types/gl/Gearbox.d.ts +86 -0
  44. package/dist/types/gl/Geometry.d.ts +39 -0
  45. package/dist/types/gl/IK.d.ts +53 -0
  46. package/dist/types/gl/InstancedMesh.d.ts +46 -0
  47. package/dist/types/gl/Light.d.ts +73 -0
  48. package/dist/types/gl/Lightmap.d.ts +85 -0
  49. package/dist/types/gl/Locomotion.d.ts +279 -0
  50. package/dist/types/gl/Material.d.ts +100 -0
  51. package/dist/types/gl/Mesh.d.ts +33 -0
  52. package/dist/types/gl/Model.d.ts +53 -0
  53. package/dist/types/gl/NavAgent.d.ts +99 -0
  54. package/dist/types/gl/NavMesh.d.ts +169 -0
  55. package/dist/types/gl/Node.d.ts +99 -0
  56. package/dist/types/gl/Noise.d.ts +20 -0
  57. package/dist/types/gl/Particles.d.ts +277 -0
  58. package/dist/types/gl/Physics.d.ts +91 -0
  59. package/dist/types/gl/Plane.d.ts +19 -0
  60. package/dist/types/gl/Ragdoll.d.ts +86 -0
  61. package/dist/types/gl/Ray.d.ts +8 -0
  62. package/dist/types/gl/Scene.d.ts +221 -0
  63. package/dist/types/gl/Shape.d.ts +122 -0
  64. package/dist/types/gl/Terrain.d.ts +280 -0
  65. package/dist/types/gl/Texture.d.ts +27 -0
  66. package/dist/types/gl/Trigger.d.ts +10 -0
  67. package/dist/types/gl/Vehicle.d.ts +191 -0
  68. package/dist/types/gl/Wheel.d.ts +95 -0
  69. package/dist/types/gl/animation/AnimationClip.d.ts +60 -0
  70. package/dist/types/gl/animation/Animator.d.ts +219 -0
  71. package/dist/types/gl/animation/Layer.d.ts +31 -0
  72. package/dist/types/gl/animation/Loop.d.ts +17 -0
  73. package/dist/types/gl/animation/Playback.d.ts +36 -0
  74. package/dist/types/gl/animation/core.d.ts +367 -0
  75. package/dist/types/gl/controls.d.ts +19 -0
  76. package/dist/types/gl/physicsEvents.d.ts +1 -0
  77. package/dist/types/gl/scenarios.d.ts +98 -0
  78. package/dist/types/gl/state.d.ts +5 -0
  79. package/dist/types/gl/terrainMesh.d.ts +46 -0
  80. package/dist/types/gl/touch.d.ts +2 -0
  81. package/dist/types/inject.d.ts +123 -0
  82. package/dist/types/math/Mathf.d.ts +39 -0
  83. package/dist/types/math/mat4.d.ts +66 -0
  84. package/dist/types/math/quat.d.ts +53 -0
  85. package/dist/types/math/vec.d.ts +129 -0
  86. package/dist/types/net/codec.d.ts +25 -0
  87. package/dist/types/net/core.d.ts +106 -0
  88. package/dist/types/net/index.d.ts +80 -0
  89. package/dist/types/net/replication.d.ts +101 -0
  90. package/dist/types/plugins/camera.d.ts +25 -0
  91. package/dist/types/plugins/geolocation.d.ts +39 -0
  92. package/dist/types/plugins/oauth.d.ts +25 -0
  93. package/dist/types/plugins/permission.d.ts +1 -0
  94. package/dist/types/plugins/push.d.ts +46 -0
  95. package/dist/types/plugins/qr.d.ts +21 -0
  96. package/dist/types/plugins/service.d.ts +33 -0
  97. package/dist/types/runtime/app.d.ts +67 -0
  98. package/dist/types/runtime/appEvents.d.ts +7 -0
  99. package/dist/types/runtime/channel.d.ts +1 -0
  100. package/dist/types/runtime/clipboard.d.ts +7 -0
  101. package/dist/types/runtime/datetime.d.ts +80 -0
  102. package/dist/types/runtime/device.d.ts +126 -0
  103. package/dist/types/runtime/fetch.d.ts +31 -0
  104. package/dist/types/runtime/files.d.ts +33 -0
  105. package/dist/types/runtime/input.d.ts +121 -0
  106. package/dist/types/runtime/media.d.ts +46 -0
  107. package/dist/types/runtime/misc.d.ts +11 -0
  108. package/dist/types/runtime/net.d.ts +15 -0
  109. package/dist/types/runtime/rpc.d.ts +44 -0
  110. package/dist/types/runtime/service.d.ts +1 -0
  111. package/dist/types/runtime/share.d.ts +2 -0
  112. package/dist/types/runtime/storage.d.ts +5 -0
  113. package/dist/types/runtime/touch.d.ts +56 -0
  114. package/dist/types/scene/defineScene.d.ts +296 -0
  115. package/dist/types/scene/editorPlugins.d.ts +82 -0
  116. package/dist/types/scene/gizmos.d.ts +56 -0
  117. package/dist/types/scene/grammar.d.ts +56 -0
  118. package/dist/types/scene/level.d.ts +98 -0
  119. package/dist/types/scene/material.d.ts +76 -0
  120. package/dist/types/server/auth/appConfig.d.ts +16 -0
  121. package/dist/types/server/auth/global.d.ts +56 -0
  122. package/dist/types/server/auth/models.d.ts +164 -0
  123. package/dist/types/server/auth/types.d.ts +56 -0
  124. package/dist/types/server/channel.d.ts +28 -0
  125. package/dist/types/server/context.d.ts +27 -0
  126. package/dist/types/server/db/defineDb.d.ts +36 -0
  127. package/dist/types/server/db/fields.d.ts +146 -0
  128. package/dist/types/server/db/index.d.ts +6 -0
  129. package/dist/types/server/db/marci/query.d.ts +326 -0
  130. package/dist/types/server/db/types.d.ts +163 -0
  131. package/dist/types/server/errors.d.ts +8 -0
  132. package/dist/types/server/inject.d.ts +5 -0
  133. package/dist/types/ui/NativeView.d.ts +38 -0
  134. package/dist/types/ui/UI.d.ts +27 -0
  135. package/dist/types/ui/UIBottomSheet.d.ts +71 -0
  136. package/dist/types/ui/UIButton.d.ts +30 -0
  137. package/dist/types/ui/UIContainer.d.ts +33 -0
  138. package/dist/types/ui/UIImage.d.ts +35 -0
  139. package/dist/types/ui/UIInput.d.ts +110 -0
  140. package/dist/types/ui/UIModal.d.ts +70 -0
  141. package/dist/types/ui/UINode.d.ts +363 -0
  142. package/dist/types/ui/UIPager.d.ts +123 -0
  143. package/dist/types/ui/UIPopover.d.ts +39 -0
  144. package/dist/types/ui/UIScreen.d.ts +57 -0
  145. package/dist/types/ui/UIScrollable.d.ts +40 -0
  146. package/dist/types/ui/UISpacer.d.ts +9 -0
  147. package/dist/types/ui/UITabs.d.ts +96 -0
  148. package/dist/types/ui/UIText.d.ts +19 -0
  149. package/dist/types/ui/UIVideo.d.ts +28 -0
  150. package/dist/types/ui/UIVirtualizedList.d.ts +97 -0
  151. package/dist/types/ui/UIWidget.d.ts +65 -0
  152. package/dist/types/ui/fonts.d.ts +9 -0
  153. package/dist/types/ui/presentable.d.ts +68 -0
  154. package/dist/types/ui/router.d.ts +40 -0
  155. package/dist/types/ui/theme.d.ts +26 -0
  156. package/dist/types.json +1 -0
  157. package/package.json +46 -0
  158. package/prompts/2d.md +395 -0
  159. package/prompts/3d-scene-files.md +113 -0
  160. package/prompts/3d-scene.md +119 -0
  161. package/prompts/3d.md +303 -0
  162. package/prompts/README.md +142 -0
  163. package/prompts/ar.md +109 -0
  164. package/prompts/canvas.md +27 -0
  165. package/prompts/compose.ts +46 -0
  166. package/prompts/concept.md +49 -0
  167. package/prompts/core-design.md +75 -0
  168. package/prompts/core.md +264 -0
  169. package/prompts/design.md +218 -0
  170. package/prompts/dist/2d-game.md +1530 -0
  171. package/prompts/dist/3d-app.md +1672 -0
  172. package/prompts/dist/ar-app.md +1548 -0
  173. package/prompts/dist/concept.md +49 -0
  174. package/prompts/dist/design.md +498 -0
  175. package/prompts/dist/ui-app.md +1134 -0
  176. package/prompts/index.md +37 -0
  177. package/prompts/intake-prompt.md +33 -0
  178. package/prompts/namer-prompt.md +12 -0
  179. package/prompts/router-prompt.md +50 -0
  180. package/prompts/select.ts +111 -0
  181. package/prompts/ui-design.md +203 -0
  182. package/prompts/ui.md +803 -0
  183. package/src/animate/animate.ts +238 -0
  184. package/src/animate/bezier.ts +138 -0
  185. package/src/animate/easings.ts +126 -0
  186. package/src/bridges.d.ts +1345 -0
  187. package/src/canvas/Canvas.ts +305 -0
  188. package/src/compile/__tests__/assetIconMacro.test.ts +219 -0
  189. package/src/compile/__tests__/assetMacro.test.ts +74 -0
  190. package/src/compile/__tests__/assetName.test.ts +55 -0
  191. package/src/compile/__tests__/compile.test.ts +287 -0
  192. package/src/compile/__tests__/detectEntry.test.ts +132 -0
  193. package/src/compile/__tests__/fontMacro.test.ts +199 -0
  194. package/src/compile/aspectMacro.ts +86 -0
  195. package/src/compile/assetIconMacro.ts +384 -0
  196. package/src/compile/assetMacro.ts +146 -0
  197. package/src/compile/assetName.ts +50 -0
  198. package/src/compile/bundler.ts +287 -0
  199. package/src/compile/compileProject.ts +127 -0
  200. package/src/compile/detectEntry.ts +128 -0
  201. package/src/compile/fontMacro.ts +459 -0
  202. package/src/compile/fontRegistry.ts +78 -0
  203. package/src/compile/header.ts +67 -0
  204. package/src/compile/index.ts +100 -0
  205. package/src/compile/libraryImports.ts +52 -0
  206. package/src/compile/liteMaterial.ts +247 -0
  207. package/src/compile/sceneEditor.ts +88 -0
  208. package/src/compile/serverSplit.ts +233 -0
  209. package/src/compile/serverTypes.ts +227 -0
  210. package/src/compile/sfnt.ts +98 -0
  211. package/src/compile/shaderSchema.ts +202 -0
  212. package/src/compile/shaderTargets.ts +77 -0
  213. package/src/compile/sourcemap.ts +25 -0
  214. package/src/core/Aspect.ts +568 -0
  215. package/src/core/InspectorUI.ts +212 -0
  216. package/src/core/StateMachine.ts +308 -0
  217. package/src/core/__tests__/stateMachine.test.ts +132 -0
  218. package/src/core/color.ts +66 -0
  219. package/src/core/compWrite.ts +42 -0
  220. package/src/core/events.ts +38 -0
  221. package/src/core/fields.ts +120 -0
  222. package/src/core/registry.ts +23 -0
  223. package/src/core/signals.ts +277 -0
  224. package/src/core/time.ts +84 -0
  225. package/src/g2/Camera2D.ts +40 -0
  226. package/src/g2/CharacterController2D.ts +276 -0
  227. package/src/g2/Node2D.ts +267 -0
  228. package/src/g2/OneWay2D.ts +66 -0
  229. package/src/g2/Physics2D.ts +346 -0
  230. package/src/g2/Scene2D.ts +209 -0
  231. package/src/g2/Shape2D.ts +259 -0
  232. package/src/g2/Sprite.ts +89 -0
  233. package/src/g2/SpriteAnimation.ts +171 -0
  234. package/src/g2/SpriteSheet.ts +166 -0
  235. package/src/g2/Texture2D.ts +47 -0
  236. package/src/g2/Tilemap.ts +41 -0
  237. package/src/g2/Tileset.ts +71 -0
  238. package/src/g2/Trigger2D.ts +77 -0
  239. package/src/g2/autotile.ts +433 -0
  240. package/src/g2/cells.ts +91 -0
  241. package/src/g2/defineScene2d.ts +381 -0
  242. package/src/g2/groups2d.ts +106 -0
  243. package/src/g2/loop.ts +50 -0
  244. package/src/g2/scenarios2d.ts +69 -0
  245. package/src/g2/touch.ts +83 -0
  246. package/src/gl/Camera.ts +160 -0
  247. package/src/gl/CameraPlace.ts +52 -0
  248. package/src/gl/CharacterController.ts +238 -0
  249. package/src/gl/Gearbox.ts +212 -0
  250. package/src/gl/Geometry.ts +279 -0
  251. package/src/gl/IK.ts +193 -0
  252. package/src/gl/InstancedMesh.ts +132 -0
  253. package/src/gl/Light.ts +135 -0
  254. package/src/gl/Lightmap.ts +402 -0
  255. package/src/gl/Locomotion.ts +498 -0
  256. package/src/gl/Material.ts +279 -0
  257. package/src/gl/Mesh.ts +83 -0
  258. package/src/gl/Model.ts +124 -0
  259. package/src/gl/NavAgent.ts +337 -0
  260. package/src/gl/NavMesh.ts +397 -0
  261. package/src/gl/Node.ts +350 -0
  262. package/src/gl/Noise.ts +30 -0
  263. package/src/gl/Particles.ts +676 -0
  264. package/src/gl/Physics.ts +222 -0
  265. package/src/gl/Plane.ts +53 -0
  266. package/src/gl/Ragdoll.ts +270 -0
  267. package/src/gl/Ray.ts +16 -0
  268. package/src/gl/Scene.ts +505 -0
  269. package/src/gl/Shape.ts +409 -0
  270. package/src/gl/Terrain.ts +1092 -0
  271. package/src/gl/Texture.ts +63 -0
  272. package/src/gl/Trigger.ts +45 -0
  273. package/src/gl/Vehicle.ts +473 -0
  274. package/src/gl/Wheel.ts +240 -0
  275. package/src/gl/animation/AnimationClip.ts +204 -0
  276. package/src/gl/animation/Animator.ts +329 -0
  277. package/src/gl/animation/Layer.ts +39 -0
  278. package/src/gl/animation/Loop.ts +31 -0
  279. package/src/gl/animation/Playback.ts +52 -0
  280. package/src/gl/animation/core.ts +670 -0
  281. package/src/gl/controls.ts +95 -0
  282. package/src/gl/physicsEvents.ts +20 -0
  283. package/src/gl/scenarios.ts +291 -0
  284. package/src/gl/state.ts +6 -0
  285. package/src/gl/terrainMesh.ts +219 -0
  286. package/src/gl/touch.ts +68 -0
  287. package/src/host.d.ts +61 -0
  288. package/src/inject.ts +203 -0
  289. package/src/math/Mathf.ts +118 -0
  290. package/src/math/mat4.ts +278 -0
  291. package/src/math/quat.ts +232 -0
  292. package/src/math/vec.ts +255 -0
  293. package/src/net/codec.ts +119 -0
  294. package/src/net/core.ts +384 -0
  295. package/src/net/index.ts +181 -0
  296. package/src/net/replication.ts +622 -0
  297. package/src/plugins/camera.ts +81 -0
  298. package/src/plugins/geolocation.ts +123 -0
  299. package/src/plugins/oauth.ts +61 -0
  300. package/src/plugins/permission.ts +7 -0
  301. package/src/plugins/push.ts +132 -0
  302. package/src/plugins/qr.ts +73 -0
  303. package/src/plugins/service.ts +47 -0
  304. package/src/runtime/app.ts +101 -0
  305. package/src/runtime/appEvents.ts +54 -0
  306. package/src/runtime/channel.ts +61 -0
  307. package/src/runtime/clipboard.ts +20 -0
  308. package/src/runtime/datetime.ts +329 -0
  309. package/src/runtime/device.ts +293 -0
  310. package/src/runtime/fetch.ts +77 -0
  311. package/src/runtime/files.ts +108 -0
  312. package/src/runtime/input.ts +175 -0
  313. package/src/runtime/media.ts +111 -0
  314. package/src/runtime/misc.ts +16 -0
  315. package/src/runtime/net.ts +36 -0
  316. package/src/runtime/rpc.ts +218 -0
  317. package/src/runtime/service.ts +83 -0
  318. package/src/runtime/share.ts +9 -0
  319. package/src/runtime/storage.ts +13 -0
  320. package/src/runtime/touch.ts +76 -0
  321. package/src/scene/defineScene.ts +1396 -0
  322. package/src/scene/editorPlugins.ts +110 -0
  323. package/src/scene/gizmos.ts +148 -0
  324. package/src/scene/grammar.ts +120 -0
  325. package/src/scene/level.ts +296 -0
  326. package/src/scene/material.ts +188 -0
  327. package/src/server/auth/appConfig.ts +12 -0
  328. package/src/server/auth/global.ts +80 -0
  329. package/src/server/auth/host.ts +318 -0
  330. package/src/server/auth/models.ts +83 -0
  331. package/src/server/auth/types.ts +50 -0
  332. package/src/server/channel.ts +56 -0
  333. package/src/server/context.ts +36 -0
  334. package/src/server/db/defineDb.ts +237 -0
  335. package/src/server/db/fields.ts +132 -0
  336. package/src/server/db/httpTransport.ts +93 -0
  337. package/src/server/db/index.ts +7 -0
  338. package/src/server/db/marci/query.ts +412 -0
  339. package/src/server/db/types.ts +202 -0
  340. package/src/server/errors.ts +12 -0
  341. package/src/server/host.ts +74 -0
  342. package/src/server/inject.ts +13 -0
  343. package/src/server/runtime.ts +133 -0
  344. package/src/server/validate.ts +87 -0
  345. package/src/ui/NativeView.ts +142 -0
  346. package/src/ui/UI.ts +39 -0
  347. package/src/ui/UIBottomSheet.ts +139 -0
  348. package/src/ui/UIButton.ts +101 -0
  349. package/src/ui/UIContainer.ts +60 -0
  350. package/src/ui/UIImage.ts +83 -0
  351. package/src/ui/UIInput.ts +185 -0
  352. package/src/ui/UIModal.ts +139 -0
  353. package/src/ui/UINode.ts +830 -0
  354. package/src/ui/UIPager.ts +362 -0
  355. package/src/ui/UIPopover.ts +100 -0
  356. package/src/ui/UIScreen.ts +123 -0
  357. package/src/ui/UIScrollable.ts +87 -0
  358. package/src/ui/UISpacer.ts +14 -0
  359. package/src/ui/UITabs.ts +236 -0
  360. package/src/ui/UIText.ts +51 -0
  361. package/src/ui/UIVideo.ts +88 -0
  362. package/src/ui/UIVirtualizedList.ts +241 -0
  363. package/src/ui/UIWidget.ts +127 -0
  364. package/src/ui/fonts.ts +13 -0
  365. package/src/ui/presentable.ts +117 -0
  366. package/src/ui/router.ts +132 -0
  367. package/src/ui/theme.ts +84 -0
@@ -0,0 +1,119 @@
1
+ ## 3D scenes (non-AR)
2
+
3
+ // ===== SCENE =====
4
+
5
+ new Scene(options?: {
6
+ ibl?: boolean // image-based ambient light; default TRUE — every scene has ambient
7
+ environmentIntensity?: number // IBL strength; default 20000
8
+ bloom?: boolean // default false
9
+ bloomIntensity?: number // default 0.2 (only with bloom: true)
10
+ skybox?: ColorInput // solid background / clear color
11
+ antialias?: boolean // 4× MSAA; default false
12
+ })
13
+ // Options are constructor-only, except: scene.skybox = '#87ceeb' (setter) and scene.setAntialias(on, scale = 4).
14
+
15
+ scene.open(): void // become THE active scene (only one renders; closes any other)
16
+ scene.close(): void
17
+ Scene.active // static: active Scene | null
18
+
19
+ scene.createOverlay(options?): Scene // second scene drawn ON TOP, sharing this camera (3D HUD layer).
20
+ // Add nodes to it; it renders with its parent — never open() it.
21
+ await scene.warmRender() // render once off-screen so shaders compile — do it during a
22
+ // loading screen if the first visible frame hitches
23
+
24
+ // The camera is YOURS here: position it, lookAt targets, or drive it from an aspect (see FollowCam below).
25
+
26
+ // ===== CHARACTER CONTROLLER =====
27
+ // Kinematic collide-and-slide player (Jolt CharacterVirtual). Needs a Shape (capsule recommended),
28
+ // does NOT use Physics. Has its OWN gravity, separate from the world's. Updates in the EARLY
29
+ // phase — input set this frame is consumed by this frame's step.
30
+ node.aspect(Shape, { capsule: { halfHeight: 0.6, radius: 0.3 } })
31
+ .aspect(CharacterController, {
32
+ speed?: 5, // horizontal speed, units/s
33
+ jumpSpeed?: 7, // take-off speed
34
+ gravity?: -20, // character-only gravity
35
+ maxSlope?: 45, // degrees; attach-time only
36
+ })
37
+ node.controller.move(x, z) // horizontal intent, each in [-1, 1]; STICKY — send (0, 0) to stop
38
+ node.controller.jump() // queued; consumed next frame if grounded
39
+ node.controller.teleport(x, y, z) // clears vertical velocity
40
+ node.controller.grounded // boolean
41
+ node.controller.velocity // Vec3 (fresh copy)
42
+ // It passes through Triggers, and (v1) isn't detected by them and isn't pointer-pickable while active.
43
+
44
+ ## 3D example
45
+
46
+ // Complete mini-game slice: physics scene, character on a ground plane, WASD + jump,
47
+ // click crates to knock them away, trigger goal zone, transparent UIText HUD.
48
+
49
+ <file name="main.ts">
50
+ Physics.configure({ gravity: [0, -9.81, 0] })
51
+
52
+ const scene = new Scene({ skybox: '#87ceeb', antialias: true })
53
+ scene.add(Light.sun({ direction: [1, -2, -1], shadowsQuality: 2 }))
54
+
55
+ // Ground: flat plane (normal up!) + static collider
56
+ scene.add(Mesh.plane({
57
+ material: Material.lit({ color: '#556655' }),
58
+ normal: [0, 1, 0], scale: 30, receiveShadows: true,
59
+ }).aspect(Shape, { box: [15, 0.05, 15] }).aspect(Physics, { motion: 'static' }))
60
+
61
+ // Crates: dynamic bodies, clickable via their Shape
62
+ let score = 0
63
+ for (let i = 0; i < 5; i++) {
64
+ const crate = Mesh.box({
65
+ material: Material.lit({ color: '#b5854b' }),
66
+ position: [i * 2 - 4, 0.5, -3], castShadows: true,
67
+ }).aspect(Shape, {}).aspect(Physics, { mass: 2 })
68
+ crate.addEventListener('click', () => {
69
+ crate.physics.applyImpulse([Mathf.random(-3, 3), 8, -6])
70
+ scoreLabel.text = `Score: ${++score}`
71
+ })
72
+ scene.add(crate)
73
+ }
74
+
75
+ // Player: capsule character controller
76
+ const hero = Mesh.cylinder({
77
+ material: Material.lit({ color: '#3878f0' }),
78
+ position: [0, 1, 3], scale: [0.6, 1.8, 0.6], castShadows: true,
79
+ })
80
+ .aspect(Shape, { capsule: { halfHeight: 0.6, radius: 0.3 } })
81
+ .aspect(CharacterController, { speed: 6, jumpSpeed: 8 })
82
+ scene.add(hero)
83
+
84
+ // Goal zone: sensor that ends the round
85
+ const goal = Mesh.box({ material: Material.unlit({ color: '#44ff88' }), position: [0, 1, -8], scale: [2, 2, 0.3] })
86
+ .aspect(Shape, {})
87
+ .aspect(Trigger)
88
+ goal.addEventListener('enter', other => {
89
+ if (other === hero) scoreLabel.text = `You win! Score: ${score}`
90
+ })
91
+ scene.add(goal)
92
+
93
+ // Camera follows the hero (LATE default phase — reads final post-physics positions)
94
+ class FollowCam extends Aspect<'followCam', Node> {
95
+ update() {
96
+ const p = this.node.worldPosition
97
+ scene.camera.position = [p.x, p.y + 3, p.z + 6]
98
+ scene.camera.lookAt(p)
99
+ }
100
+ }
101
+ hero.aspect(FollowCam)
102
+
103
+ setLoop(() => {
104
+ const x = (Input.key('KeyD') ? 1 : 0) - (Input.key('KeyA') ? 1 : 0)
105
+ const z = (Input.key('KeyS') ? 1 : 0) - (Input.key('KeyW') ? 1 : 0)
106
+ hero.controller.move(x, z)
107
+ if (Input.key('Space')) hero.controller.jump()
108
+ })
109
+
110
+ // HUD over the 3D view: a UIWidget ATTACHED to the scene. (A UIScreen would REPLACE the scene —
111
+ // exactly one Presentable is visible at a time.)
112
+ let scoreLabel: UIText
113
+ const hud = UIWidget(
114
+ scoreLabel = UIText('Score: 0').style({ color: 'white', fontSize: 24, fontWeight: 700 }),
115
+ ).style({ top: 'max(safe-top, 16px)', right: 16 })
116
+
117
+ scene.open()
118
+ hud.attachTo(scene).show()
119
+ </file>
package/prompts/3d.md ADDED
@@ -0,0 +1,303 @@
1
+ ## 3D engine
2
+
3
+ Filament-rendered 3D content with Jolt physics. Units are world units ≈ METERS, Y is up, forward is −Z. All `eulerAngles` are DEGREES. Everything here is a global — no imports.
4
+
5
+ Scene construction and lifecycle are mode-specific — see the Scene (or AR) section further below. Everything in THIS section applies to any scene:
6
+
7
+ // ===== SCENE CONTENT (any scene kind) =====
8
+
9
+ scene.add(...nodes: Node[]): this // register with the draw set (≠ parenting — see hierarchy)
10
+ scene.remove(...nodes: Node[]): this
11
+
12
+ // Scene-level pointer events — fire for EVERY tap; ev.target = hit Node (needs a Shape) or null:
13
+ scene.addEventListener('click' | 'touchstart', (ev) => { ev.target; ev.clientX; ev.clientY })
14
+
15
+ // 2D HUD over a 3D view: open a UIScreen (no bgColor → transparent) AFTER the scene opens — see UI section.
16
+
17
+ // ===== NODES =====
18
+ // Node = empty transform. Mesh, Model, Light, Camera, Particles all extend it.
19
+ // The NATIVE engine owns the transform (physics rewrites it) — getters return fresh COPIES (see math).
20
+
21
+ const pivot = new Node()
22
+ pivot.position = [0, 1, -2] // Vec3Like (raw arrays fine); getters return Vec3 copies
23
+ pivot.x = 3 // scalar setters x / y / z — the way to move one axis
24
+ pivot.quaternion = Quat.fromEuler(0, 90, 0)
25
+ pivot.eulerAngles = [0, 90, 0] // DEGREES
26
+ pivot.scale = 2 // uniform number or [sx, sy, sz]
27
+ pivot.matrix = Mat4.compose([0, 1, 0], Quat.identity, 1)
28
+
29
+ node.worldMatrix // get/set full world transform
30
+ node.worldPosition / node.worldQuaternion / node.worldEulerAngles / node.worldScale // read-only
31
+ node.forward // Vec3: world-space −Z axis (read-only)
32
+
33
+ node.name = 'door' // visible/name; traverse lookups match on name
34
+ node.visible = false
35
+ node.destroy() // frees the native entity — the ONLY teardown; node unusable after
36
+
37
+ // Hierarchy (separate from scene membership — scene.add puts it in the world, node.add parents it):
38
+ node.add(...children): this
39
+ node.setParent(parent: Node | null, worldPositionStays = false): this
40
+ node.parent / node.children / node.childCount / node.getChild(i)
41
+ node.traverse(cb: (node: Node) => void) // this node, then all descendants
42
+
43
+ node.lookAt(point: Vec3Like, mode = '-z', up = [0, 1, 0]): this // aim a local axis at a WORLD point
44
+
45
+ // Node events — node.addEventListener(channel, cb) / removeEventListener:
46
+ // 'click' pointer-up over the node — REQUIRES a Shape aspect (pick body)
47
+ // 'touchstart' pointer-down over the node (ev.track() drags) — requires Shape
48
+ // 'enter' / 'exit' (other: Node) physics contact / trigger overlap began/ended — Shape + Physics/Trigger
49
+ // Without a Shape a node is INVISIBLE to taps — only scene listeners fire (ev.target === null).
50
+
51
+ // ===== ASPECTS =====
52
+ // Capabilities attach to nodes as aspects and chain; each adds a named accessor:
53
+ node.aspect(Class, opts?) // attach + configure; returns the node (typed with the accessor)
54
+ node.get(Class) / node.has(Class) / node.removeAspect(Class)
55
+ // Named accessors: node.physics, node.trigger, node.controller, node.shape, node.anim
56
+ // Custom game logic = your own aspect with per-frame update(dt seconds):
57
+ class Spin extends Aspect<'spin', Node> { // <accessor name, node kind>
58
+ speed = 90 // class fields = configurable defaults (opts override)
59
+ update(dt: number) { this.node.eulerAngles = this.node.eulerAngles.add([0, this.speed * dt, 0]) }
60
+ }
61
+ mesh.aspect(Spin, { speed: 45 }) // never `new Spin()`; init in onAttach(), clean in onDetach()
62
+ // Phases: default = LATE (after physics — for cameras/followers reading final positions);
63
+ // set `updateBeforePhysics = true` on aspects that WRITE velocity/kinematic transforms.
64
+
65
+ // ===== MESHES & MATERIALS =====
66
+
67
+ // Primitive factories — all take: { material? (default Material.unlit()), position?, eulerAngles?,
68
+ // scale?, name?, castShadows?, receiveShadows? }. Defaults are UNIT-sized (1 m).
69
+ Mesh.box({ size?: Vec3Like | number }) // full edge lengths; default 1×1×1
70
+ Mesh.sphere({ radius? }) // default 0.5 (unit diameter)
71
+ Mesh.cylinder({ radius?, radiusTop?, radiusBottom?, edges? }) // height fixed 1; radiusTop: 0 = cone
72
+ Mesh.plane({ normal?: Vec3Like }) // 1×1 quad; default normal [0,0,1] = STANDS UPRIGHT facing camera.
73
+ // A ground/floor plane NEEDS normal: [0, 1, 0].
74
+
75
+ mesh.material // get/set slot 0; mesh.setMaterial(mat, index = 0)
76
+ mesh.castShadows = true // write-only render flags (or set in factory options)
77
+ mesh.receiveShadows = true
78
+
79
+ // Materials:
80
+ Material.lit({ color?, map?: Texture }) // PBR — needs light (IBL and/or sun) or renders black
81
+ Material.unlit({ color?, map? }) // flat, ignores all lighting
82
+ Material.video(player.texture) // samples a VideoPlayer's texture
83
+ Material.shadow(color = '#000000aa') // shadow-catcher: invisible except where shadows fall (AR staple)
84
+ material.color = '#ff8800' // convenience setters on lit/unlit (no-op on custom shaders)
85
+ material.map = tex // Texture | Canvas | null
86
+
87
+ // Custom compiled shaders (.mat files) — load, then set uniforms by name (chainable):
88
+ const mat = new Material(await fetch(asset('./hologram.mat'), { useOnce: true }))
89
+ .set('baseColor', '#ffa200') // '#rrggbb' / '#rrggbbaa' hex STRINGS only
90
+ .set('roughness', 0.54) // number → float, boolean → bool
91
+ .set('glowMap', tex) // Texture → sampler
92
+ .set('dir', [0, 1, 0]) // number[] / Float32Array → vector. NOT a Vec3 — spread it: [...v]
93
+ // material.uniforms.key = value — same thing property-style. `color`/`map` setters don't know a
94
+ // custom shader's uniform names — always .set() the real names there.
95
+
96
+ // Textures:
97
+ const tex = await Texture.load(asset('./crate.jpg')) // url string | FetchResponse | File
98
+ tex.wrapS = 1; tex.wrapT = 1 // set BEFORE assigning to a material
99
+ Texture.fromCanvas(c) // bake a Canvas (see CANVAS section) — nameplates, generated art;
100
+ // material.map = c does the same implicitly; c.update() refreshes it
101
+ // No dispose() — textures live until the engine tears down. Load once, reuse everywhere.
102
+
103
+ // Light — Light.sun() is the ONLY light (no point/spot lights). Ambient comes from the scene's IBL.
104
+ scene.add(Light.sun({
105
+ direction?: Vec3Like, // where light TRAVELS; default a key-light angle
106
+ intensity?: number, // default 100000
107
+ color?: ColorInput,
108
+ shadowsQuality?: 0 | 1 | 2 | 3 // 0 = none, 1 = default 1024 map, 2 = soft 2048, 3 = PCSS 4096
109
+ }))
110
+ // Creation-time only — no setters; to change the sun, destroy() it and add a new one.
111
+ // Shadows also need castShadows on the caster + receiveShadows on the ground.
112
+
113
+ // Custom geometry — tweak a primitive before wrapping (moves pivot, tiles UVs), then Mesh.from(geo):
114
+ const geo = Geometry.cylinder({ radiusTop: 0, edges: 6 }).scale(1, 3, 1).translate(0, 1.5, 0)
115
+ const spike = Mesh.from(geo, { material }) // finish geometry BEFORE building the mesh (buffers upload once)
116
+
117
+ // ===== MODELS (GLB) =====
118
+
119
+ const hero = await Model.load(asset('./hero.glb')) // asset path | https URL | FetchResponse; rejects on error
120
+ scene.add(hero) // a Model IS a Node — transform/events/aspects all apply
121
+ hero.traverse(n => { if (n.name === 'Sword') n.visible = false }) // GLB internals are plain child Nodes
122
+
123
+ // Animation — every Model has an Animator pre-attached at model.anim (clip list = the glb's clips). A layer
124
+ // has a LOOP (what it rests on) and ONE-SHOTS played over it; any call overrides what's there over its own fade:
125
+ hero.anim.clips / hero.anim.clip('Run') / hero.anim.clip(0) // AnimationClip[] (file order) / by name / by index; unnamed clips = 'animation_0', …
126
+ hero.anim.playLoop('Idle', { fade: 0.2 }) // rest on a looping clip (crossfades from whatever plays); hero.anim.loop reads it
127
+ const loco = hero.anim.playLoop({ Idle: 0, Walking: 2, 'Fast Run': 6 }) // a BLEND as the loop (1D; [x, y] positions = 2D)
128
+ loco.value = speed // position on the axis — members stay in phase (no foot sliding)
129
+ const ok = await hero.anim.play('slash', { fadeIn: 0.1, fadeOut: 0.3 }) // one-shot over the loop, returns to it
130
+ // fades default to 0 (cut): fade = both ends, fadeIn / fadeOut = one end. Unknown name warns, plays nothing.
131
+ // The await resolves at the HAND-OVER (end − fadeOut) with true (false = cut short) — what you start right after is what
132
+ // the clip fades into: chains crossfade: if (ok) await hero.anim.play('slash2', { fadeIn: 0.3, fadeOut: 0.3 })
133
+ // then hero.anim.playLoop('Crouch', { fade: 0.3 }) — or nothing = back to the current loop.
134
+ if (!hero.anim.busy) hero.anim.play('kick') // busy = a one-shot hasn't handed over; play() always takes over otherwise
135
+ hero.anim.stop({ fade: 0.2 }) // everything off → rest; hero.anim.busy; hero.anim.speed = 0.3 (0 = pause)
136
+ // p = play(): p.done (return THIS from async fns) / p.playing / p.progress / p.weight / p.stop({ fade }) / p.seek(t)
137
+ // Clips from other files (Mixamo: one GLB per animation), procedural, sliced — add by name:
138
+ const [idle, slash] = await Promise.all([AnimationClip.load(asset('./idle.glb')), AnimationClip.load(asset('./attacks.glb'), 'Slash')])
139
+ hero.anim.addClip('slash', slash).addClip('kick', hero.anim.clip('Kick').slice(0.2, 1.1)) // slice = a sub-range, re-timed
140
+ AnimationClip.from({ tracks: { Hips: { position: [[0, [0,0,0]], [1, [0,0.05,0]]] } } }) // curves in code, binds by node NAME
141
+ // Clip events live on the CLIP, in SECONDS: slash.addEvent(0.4, 'hit') → hero.anim.on('hit', (clip, layer) => dealDamage())
142
+ // Layers (legs walk, arms aim): const upper = hero.anim.addLayer({ mask: 'Spine1' }); upper.playLoop('Aim', { fade: 0.3 }); upper.stop()
143
+ // { additive: true } = each clip's DELTA vs its first frame on top (recoil, breathe); upper.weight = 0.5
144
+ // Root motion (clips whose hips travel — Mixamo without 'In Place', rolls): hero.anim.rootMotion = true → the node moves
145
+ // (or its CharacterController, as a velocity — it collides); the bone stays put
146
+ hero.bone('RightHand')?.add(sword) // bones are Nodes — sockets
147
+ // FBX assets (Mixamo exports FBX): `lecodes assets convert hero.fbx --clips Idle.fbx Run.fbx -o hero.glb` → one GLB, clips merged by bone name
148
+ // IK — late-phase aspects ON BONES (over the Animator's pose): TwoBone on the END bone, LookAt on the bone itself
149
+ hero.bone('LeftFoot').aspect(IK.TwoBone, { target: footPoint, pole: kneeHint }).ik.weight = grounded ? 1 : 0
150
+ hero.bone('Head').aspect(IK.LookAt, { target: camera, limit: 70 }) // axis = bone-local forward, default [0,0,-1]
151
+
152
+ // ===== PHYSICS (Jolt) =====
153
+
154
+ Physics.supported // static boolean — without it EVERYTHING here silently no-ops
155
+ // (aspects attach fine, velocity reads [0,0,0], no events) — gate gameplay
156
+ Physics.configure({ gravity?: [0, -9.81, 0], maxBodies?: 4096 }) // ONCE, BEFORE any body exists
157
+ Physics.interpolation = true // static; default on — smooths bodies between fixed 60 Hz steps
158
+
159
+ // Shape — collision geometry AND pickability. MUST be attached BEFORE Physics/Trigger/CharacterController
160
+ // (they throw "requires a Shape aspect" otherwise). Pick exactly one kind:
161
+ node.aspect(Shape, {
162
+ box?: Vec3Like, // HALF-extents, WORLD units, NOT scaled by the node
163
+ sphere?: number, // radius
164
+ cylinder?: { halfHeight: number, radius: number }, // Y-aligned
165
+ capsule?: { halfHeight: number, radius: number }, // halfHeight = cylinder section only
166
+ mesh?: true | 'convex', // the node's OWN triangles (Mesh geometry or Model GLB, × world scale)
167
+ origin?: Vec3Like, // collider centre, offset from the node's pivot (world units, node rotation, NOT scaled)
168
+ raycast?: boolean, // default true; false = invisible to taps & raycasts
169
+ })
170
+ // mesh: true = exact triangle mesh → level geometry / terrain / ramps: static or kinematic Physics, Trigger,
171
+ // pick bodies, character ground. NEVER dynamic (Physics throws). mesh: 'convex' = convex hull → dynamic props.
172
+ // A Model collider is its bind pose (skinned parts skipped) — animated characters keep a capsule.
173
+ node.aspect(Shape, {}) // auto: box from the mesh AABB × world scale — right for most meshes
174
+ // origin: art modelled ABOVE its pivot (a character on its feet, a barrel on its base) needs the
175
+ // collider lifted: { capsule: {…}, origin: [0, 0.9, 0] }. An auto shape already centres on its mesh.
176
+ node.shape.fit(kind?) // measure dimensions + origin from what the node RENDERS (subtree,
177
+ // so a Model root works where {} can't) and rebuild the live collider. Fit AFTER Model.load resolves.
178
+ // A bare Shape (no Physics) creates a STATIC pick-only body → the node gets 'click'/'touchstart'.
179
+ // It snapshots the transform at attach time — a MOVING pickable object needs a Physics body too.
180
+
181
+ // Physics — rigid body:
182
+ node.aspect(Shape, {}).aspect(Physics, {
183
+ motion?: 'static' | 'dynamic' | 'kinematic', // default 'dynamic'
184
+ mass?: number, // kg, dynamic only; default 1
185
+ })
186
+ node.physics.velocity // Vec3 world units/s — get (fresh copy) / set
187
+ node.physics.angularVelocity // Vec3 DEGREES/s about each world axis — get / set
188
+ node.physics.applyImpulse(v): this // instant impulse, wakes the body
189
+ node.position = [x, y, z] // place / teleport — moves the node AND its body (no moveTo())
190
+ node.eulerAngles = [0, 90, 0] // orientation is routed to the body too (NOT for a CharacterController)
191
+ // A teleport keeps BOTH velocities: putting an object down is velocity = 0, angularVelocity = 0, position = p
192
+ // PHYSICS OWNS a dynamic body's transform: a position write teleports, but the sim takes over again —
193
+ // drive motion with velocity / applyImpulse. READING node.position/worldPosition is always correct.
194
+ // static = never moves (floors, walls); kinematic = write position each frame, pushes but isn't pushed.
195
+
196
+ // Contact events — on BOTH nodes of a contact/overlap, arg = the other node:
197
+ crate.addEventListener('enter', (other: Node) => {})
198
+ crate.addEventListener('exit', (other: Node) => {})
199
+
200
+ // Trigger — static sensor zone (needs a Shape; no options). Overlapping bodies fire 'enter'/'exit'
201
+ // instead of colliding; never blocks movement:
202
+ const goal = Mesh.box({ position: [0, 1, -6] }).aspect(Shape, { box: [1, 1, 0.2] }).aspect(Trigger)
203
+ goal.addEventListener('enter', other => win(other))
204
+ goal.trigger.moveTo(p)
205
+
206
+ // Raycast — closest PICKABLE body (any Shape with raycast: true, incl. pick-only & triggers):
207
+ Physics.raycast(origin: Vec3Like, dir: Vec3Like, maxDist = 1000)
208
+ // → { node: Node | null, point: Vec3, normal: Vec3, fraction: number /* 0..1 of maxDist×|dir| */ } | null
209
+
210
+ // ===== PARTICLES =====
211
+ // GPU particle emitter node — move/rotate the node to move the emitter. Every option is a live setter.
212
+ new Particles({
213
+ material: Material, // REQUIRED — Material.unlit() is the usual choice
214
+ rate?: number, // particles/second (plain number only — a range is ignored)
215
+ shape?: { type: 'point', v: Vec3Like } | { type: 'box', min: Vec3Like, max: Vec3Like },
216
+ startVelocity?: Vec3Like | { // fixed vector, or a direction mode:
217
+ dir?: Vec3Like, from?: Vec3Like, to?: Vec3Like, // along / away-from / toward
218
+ speed?: number | { min, max },
219
+ randomizeAngle?: { min: Vec3Like, max: Vec3Like }, // jitter, degrees per axis
220
+ },
221
+ gravity?: number, drag?: number,
222
+ lifetime?: number | { min, max }, // seconds; range = random per particle
223
+ color?: string | { min, max } | DynamicColor,
224
+ size?: number | { min, max } | DynamicValue,
225
+ rotation?: number | { min, max } | DynamicValue,
226
+ noise?: { strength?, frequency?, speed? } | null, // turbulence
227
+ })
228
+ sparks.spawn(count) // burst on top of rate; one-shot effect = rate: 0 + spawn(n)
229
+ // Curves over each particle's life (t runs 0..1): flat (t, value, t, value, …) stop lists:
230
+ sparks.size = dynamic(0.1, 'multiply', 0, 1, 1, 4) // grow to 4× by death
231
+ sparks.color = dynamicColor('#ffaa33', 0.7, '#ffffff', 1, '#000000') // multiplies base — fade to black
232
+ sparks.destroy() // from Node — removes the system
233
+
234
+ // ===== CAMERA / RAY / PLANE / NOISE =====
235
+
236
+ scene.camera // a Node — never constructed; every scene owns one.
237
+ // (Who MOVES it is mode-specific — see the Scene/AR section.)
238
+ camera.fov / .near / .far // lens: vertical FOV degrees (60), clip 0.01 / 1000 — SETTABLE.
239
+ camera.setProjection({ fov, near, far }) // any subset; .horizontalFov, .displaySize stay read-only
240
+ camera.getViewDirection(screenX, screenY): Vec3 // world dir through a screen point (logical px = ev.clientX/Y)
241
+ camera.getRay(screenX, screenY): Ray // origin = camera.worldPosition + that direction
242
+
243
+ new Ray(origin, dir) // .origin, .dir, .getPoint(t) → origin + t·dir
244
+ new Plane(normal, point) // infinite math plane (NOT a physics body)
245
+ plane.intersectRay(ray): number | null // returns the PARAMETER t, not the point → ray.getPoint(t)
246
+ plane.signedDistance(p) / plane.projectPoint(p)
247
+
248
+ // Drag-on-ground recipe — tap/drag to a world point on the floor:
249
+ const ground = new Plane([0, 1, 0], [0, 0, 0])
250
+ scene.addEventListener('click', ev => {
251
+ const ray = scene.camera.getRay(ev.clientX, ev.clientY)
252
+ const t = ground.intersectRay(ray)
253
+ if (t !== null) marker.position = ray.getPoint(t)
254
+ })
255
+ // Against real bodies instead: Physics.raycast(ray.origin, ray.dir, 100)
256
+
257
+ const noise = new Noise() // native Perlin/fractal; deterministic — animate by moving the sample point
258
+ noise.get(x, y) / noise.get3d(x, y, z)
259
+ noise.frequency = 0.01; noise.octaves = 1 // + fractalLacunarity, fractalGain
260
+
261
+ // ===== COMMON MISTAKES — DO NOT DO THESE =====
262
+
263
+ // ❌ wrong accessor name
264
+ node.body.applyImpulse(...) // WRONG — the accessor is node.physics
265
+ // ✅ node.physics / node.trigger / node.controller / model.anim
266
+
267
+ // ❌ writing a dynamic body's transform per frame — physics owns it and fights back
268
+ setLoop(() => { crate.position = target })
269
+ // ✅ drive the simulation: crate.physics.velocity = ... / applyImpulse(...); kinematic → moveTo(...)
270
+
271
+ // ❌ Physics/Trigger/CharacterController before Shape — throws
272
+ node.aspect(Physics).aspect(Shape, {})
273
+ // ✅ Shape FIRST
274
+ node.aspect(Shape, {}).aspect(Physics)
275
+
276
+ // ❌ full extents in Shape box — collider twice the mesh
277
+ Mesh.box().aspect(Shape, { box: [1, 1, 1] }) // 2×2×2 collider around a 1×1×1 box
278
+ // ✅ HALF-extents, or {} to auto-fit
279
+ Mesh.box().aspect(Shape, { box: [0.5, 0.5, 0.5] })
280
+
281
+ // ❌ cloning a Model
282
+ const copy = model.clone() // doesn't exist
283
+ // ✅ pool: Promise.all(Array.from({ length: N }, () => Model.load(url))) up front, recycle
284
+
285
+ // ❌ a ground plane with the default normal — it stands upright facing the camera
286
+ Mesh.plane({ scale: 20 })
287
+ // ✅ Mesh.plane({ scale: 20, normal: [0, 1, 0] })
288
+
289
+ // ❌ Vec3/Quat as a custom-shader uniform value — lowers to NULL
290
+ mat.set('lightDir', dir)
291
+ // ✅ spread to a plain array: mat.set('lightDir', [...dir])
292
+
293
+ // ❌ point/spot lights — they don't exist
294
+ Light.point(...)
295
+ // ✅ Light.sun() + scene IBL is the whole lighting model; fake glows with unlit/bloom/particles
296
+
297
+ // ❌ rgba()/named colors in 3D APIs — silently black
298
+ Material.lit({ color: 'rgba(255, 0, 0, 0.5)' })
299
+ // ✅ hex string or packed int: '#ff0000' / '#ff000080' / 0xff0000
300
+
301
+ // ❌ a fresh Material per frame (or per particle) to animate a look
302
+ setLoop(() => { mesh.material = Material.lit({ color: next() }) })
303
+ // ✅ mutate the one material: mesh.material.color = next() / mat.set('progress', t)
@@ -0,0 +1,142 @@
1
+ # Codegen prompts
2
+
3
+ > **Scope: the le.codes PLATFORM assistant only.** These prompts are bundled into `dist/` and served
4
+ > by claude-proxy to the in-editor chat. They are NOT used by local development — Claude Code and
5
+ > `lecodes-cli` never read them; those get the SDK surface from `.lecodes/types` (the d.ts bundle),
6
+ > the generated project `CLAUDE.md` (`lecodes-cli/src/commands/projectTemplates.ts`) and `sdk/docs`.
7
+ > A change meant for local development belongs there, not here.
8
+
9
+ The system prompt(s) sent to Claude for LeCodes code generation, split into composable modules so
10
+ a request only pays for the API areas it can actually use. Facts come from `../docs/` (the source
11
+ of truth) — when the SDK changes, update docs first, then mirror the change here.
12
+
13
+ **Consumers:**
14
+ - `claude-proxy/prompts/*.md` — deployed copies of `dist/` + `router.md` (see its README); the
15
+ proxy injects them per the `x-lecodes-bundle` header (`claude-proxy/src/proxy/bundles.ts`). An
16
+ unknown/missing header is a **400** (no silent fallback), and the proxy's `/health` exposes a
17
+ sha256-12 per bundle — `bun run check:prompts` (repo root) compares them against `dist/` to
18
+ catch deploy drift (`--dir <proxy>/prompts` checks a local checkout instead).
19
+ - `packages/frontend/.../chat/engine/bundleSelect.ts` — a thin adapter over `select.ts` (imported
20
+ as `sdk/prompts/select`); the selection logic itself is not duplicated anywhere.
21
+
22
+ ## Layout
23
+
24
+ ```
25
+ core.md shared foundation: response format, entry-point rule, runtime, math, tweens
26
+ index.md one-line map of EVERY global — always included (anti-hallucination anchor)
27
+ canvas.md Canvas drawing surface — engine-independent, in every bundle (each engine
28
+ module documents its own hookup: Sprite texture / UIImage src / Texture)
29
+ ui.md UI module (screens, router, styling, widgets, lists)
30
+ 2d.md 2D engine module (sprites, tilemaps, Box2D physics, aspects, Canvas)
31
+ 3d.md SHARED 3D content (nodes, meshes, models, materials, physics, particles)
32
+ 3d-scene.md non-AR scenes: Scene construction, camera control, CharacterController, example
33
+ 3d-scene-files.md declarative .scene.ts files (defineScene/use/ref/make, SceneHandle, HUD-over-scene)
34
+ — the visual scene editor's format; non-AR only (camera nodes, skybox env)
35
+ ar.md AR scenes: ARScene, anchors, placement controls, example — never with 3d-scene.md
36
+ core-design.md design mode's core: directives/context/reports, no runtime APIs (no timers/network)
37
+ ui-design.md design mode's UI: curated static-mockup subset of ui.md (KEEP IN SYNC with it)
38
+ design.md design mode: canvas conventions — kit-first, states, design_map tool, tab bar
39
+ concept.md concept mode: product-shaping conversation, writes ONLY design/spec.md
40
+ compose.ts builds dist/<bundle>.md from the modules
41
+ select.ts deterministic bundle detection from project code
42
+ router-prompt.md fallback classifier prompt (empty projects only)
43
+ namer-prompt.md project name + icon-emoji generator (first request of a new project)
44
+ intake-prompt.md new-project intake: clarifying questions + confirmable brief (two Haiku turns)
45
+ ```
46
+
47
+ ## Bundles (`bun run prompts/compose.ts` → `dist/`)
48
+
49
+ | Bundle | Modules | For |
50
+ |---|---|---|
51
+ | `ui-app` | core + index + canvas + ui | apps: screens, forms, lists, chat, commerce |
52
+ | `2d-game` | core + index + canvas + 2d + ui | flat games, drawing toys |
53
+ | `3d-app` | core + index + canvas + 3d + 3d-scene + 3d-scene-files + ui | 3D games/viewers, scene-editor projects |
54
+ | `ar-app` | core + index + canvas + 3d + ar + ui | AR experiences |
55
+ | `design` | core-design + ui-design + design | design mode: the design/ prototyping canvas |
56
+ | `concept` | concept | concept mode: shape the idea, maintain design/spec.md (~0.8k tokens) |
57
+
58
+ Fixed bundles (not per-request assembly) are deliberate: **each bundle is a stable prompt prefix**,
59
+ so Anthropic prompt caching hits on every generation after the first (~10× cheaper input, lower
60
+ latency). Per-request cherry-picking would produce near-unique prefixes and always pay full price.
61
+ Game bundles include the full UI module because games need HUDs/menus; the ui-app bundle omits the
62
+ engines entirely.
63
+
64
+ ## Selecting the bundle
65
+
66
+ `design` and `concept` are different from the engine bundles: they are selected EXPLICITLY by the
67
+ client from the editor's mode switcher (Concept / Design / Build — `x-lecodes-bundle: design` or
68
+ `concept`) — never routed, detected from code, or reachable via the `<bundle>` escalation. The
69
+ engine ladder below applies to Build mode only. Concept turns also carry no code context (design
70
+ excerpts + file names only) and the client enforces each mode's write boundary (concept:
71
+ design/spec.md only; design: design/ only). Any bundle may end a reply with `<mode>x</mode>` — the
72
+ client strips it and renders a "Switch to …" button; the mode never switches itself.
73
+
74
+ For the engine bundles — three signals, in this order:
75
+
76
+ **1. Project code — `select.ts` (primary, deterministic).**
77
+ `detectBundle(files)` scans the project's `.ts` sources for engine globals (comments and string
78
+ literals stripped) and returns the bundle. This is language-independent — it doesn't matter what
79
+ language the user writes in, because it never reads the user's words at all. It covers every
80
+ request against an existing project, i.e. the vast majority of traffic. Priority: AR > 3D > 2D;
81
+ sources with no engine references → `ui-app`. This mirrors the engine-kind detection the compiler
82
+ already does for project icons — if that detection is exposed server-side, reuse it instead.
83
+
84
+ **0. Intake — `intake-prompt.md` (new projects, preferred over the router).**
85
+ The session's first request against a project with no app code runs the intake instead of building:
86
+ one Haiku call returns 2–4 clarifying questions with tappable options (including the app kind when
87
+ the request is ambiguous between kinds), a second call turns the answers into a short brief the
88
+ user confirms. The confirmed kind sets the first build turn's bundle explicitly (`x-lecodes-bundle`
89
+ sent by the client — the router below never runs), and the brief seeds `design/spec.md` as the
90
+ project concept. Intake failure or the user's Skip degrades to the flow below — it never blocks.
91
+ Client side: `frontend/.../chat/engine/intake.ts` + the intake card in `chatController.ts`.
92
+
93
+ **2. Router model — `router-prompt.md` (fallback: intake skipped/failed on empty projects).**
94
+ When `detectBundle` returns `null` (no code yet), classify the user's first message with one cheap
95
+ call: `claude-haiku-4-5`, temperature 0, `max_tokens: 8`, reply is a single word `ui|2d|3d|ar`.
96
+ The prompt is meaning-based and multilingual (EN/RU examples included). Parse defensively; any
97
+ garbage → `ui`. Cost/latency: ~1k input tokens, a few output tokens, ~200–400 ms — negligible, and
98
+ it runs at most once per project's life.
99
+
100
+ **Stickiness.** Cache the chosen bundle per conversation and only move UP
101
+ (`upgradeBundle(current, detected)`: ui → 2d/3d → ar), re-running `detectBundle` over files the
102
+ assistant itself just generated. Never downgrade mid-conversation — it would invalidate the prompt
103
+ cache and confuse in-flight context. Wrong-but-bigger is harmless; wrong-but-smaller causes
104
+ hallucinated APIs.
105
+
106
+ **3. Escalation — the `<bundle>` directive (recovery from a misroute).**
107
+ Code-based detection can never recover from a wrong-but-smaller bundle on its own: a model that
108
+ wasn't shown an engine never emits its globals, so the upgrade never triggers. core.md ("Missing
109
+ engine") therefore instructs the generator to reply with ONLY `<bundle>ar</bundle>` (or `3d`/`2d`)
110
+ when the request needs engine APIs its prompt doesn't document. The client (`parseBundleDirective`
111
+ in `select.ts`) discards that reply, upgrades the pinned bundle, and re-sends the same request
112
+ once — upgrade-only here too; a non-upgradable directive (e.g. a 3D project asking for the 2D
113
+ engine) surfaces as a plain explanation instead. Relatedly, the router's classifier input must be
114
+ ONLY the user's words: the proxy cuts everything before the frontend's `[Request]` marker, so e.g.
115
+ a `.glb` in the project snapshot can't drag an explicit AR request toward `3d`.
116
+
117
+ ## Naming new projects
118
+
119
+ `namer-prompt.md` is unrelated to bundle selection but lives here with the other model prompts:
120
+ an assistant request made while the project has NO CODE yet (no non-empty `.ts` file — uploaded
121
+ assets like a `.glb` model don't count as "started") also fires one extra Haiku call in parallel
122
+ (`x-lecodes-bundle: namer`, `chat/engine/projectNamer.ts`) that returns bare JSON `{name, emoji}`;
123
+ the result is saved via the normal project-update API (name + `iconEmoji`, drawn by
124
+ `VProjectIcon`). Uncached — the prompt is far below Haiku's minimum cacheable prefix. Parsing is
125
+ defensive: a garbled reply must never rename the project, so it's rejected rather than truncated
126
+ into place; a rename while the call is in flight also wins over the generated name.
127
+
128
+ **Why not route every request with a model?** It adds a failure mode (misroute → the generator
129
+ confidently invents APIs it wasn't shown), latency, and cost on 100% of traffic to solve a problem
130
+ that exists only for the ~one request per project where no code exists. Residual misroutes are
131
+ recoverable: the index.md map means the generator always knows what exists, and the `<bundle>`
132
+ escalation directive (above) lets it swap in the right engine module instead of guessing.
133
+
134
+ ## Keeping prompts honest
135
+
136
+ - `index.md` must list every global exported from `sdk/src/inject.ts` — same contract as
137
+ `docs/check.mjs`. When a global is added, update docs, then the module, then index.md.
138
+ - Known corrections baked into these prompts (do not regress; see `docs/AUTHORING.md`):
139
+ `onEndReached(thresholdPx, cb)` (threshold first), `bgSize: cover|contain|tile` (no `fill`),
140
+ track `deltaX/deltaY` are per-move, engine colors are hex/packed-int only, `animate` can't
141
+ tween strings/colors, `node.physics` (not `.body`), no `Camera2D.follow`, no
142
+ `UIScrollable.scrollTo`, models can't be cloned.