lecodes-sdk 1.1.0 → 2.0.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 (257) hide show
  1. package/README.md +5 -2
  2. package/dist/editor.d.ts +12 -0
  3. package/dist/global.d.ts +4 -8
  4. package/dist/host.d.ts +2 -3
  5. package/dist/types/animate/tween/Animation.d.ts +0 -3
  6. package/dist/types/animate/tween/animateValue.d.ts +4 -2
  7. package/dist/types/animate/tween/easing.d.ts +8 -0
  8. package/dist/types/animate/tween/spec.d.ts +15 -7
  9. package/dist/types/audio/audio.d.ts +2 -1
  10. package/dist/types/canvas/Canvas.d.ts +40 -108
  11. package/dist/types/canvas/gen/cssColor.d.ts +17 -0
  12. package/dist/types/canvas/gen/recorder.d.ts +118 -0
  13. package/dist/types/canvas/gen/spec.d.ts +144 -0
  14. package/dist/types/core/color.d.ts +3 -1
  15. package/dist/types/core/pins.d.ts +18 -0
  16. package/dist/types/g2/Node2D.d.ts +5 -8
  17. package/dist/types/g2/Scene2D.d.ts +6 -2
  18. package/dist/types/gl/Foliage.d.ts +30 -7
  19. package/dist/types/gl/Light.d.ts +11 -0
  20. package/dist/types/gl/Lightmap.d.ts +13 -2
  21. package/dist/types/gl/Material.d.ts +17 -3
  22. package/dist/types/gl/Model.d.ts +6 -2
  23. package/dist/types/gl/Node.d.ts +3 -6
  24. package/dist/types/gl/Particles.d.ts +15 -0
  25. package/dist/types/gl/Scene.d.ts +41 -13
  26. package/dist/types/gl/Texture.d.ts +1 -1
  27. package/dist/types/gl/animation/Locomotion.d.ts +59 -3
  28. package/dist/types/gl/terrain/Terrain.d.ts +8 -0
  29. package/dist/types/inject.d.ts +11 -11
  30. package/dist/types/inject.editor.d.ts +1 -0
  31. package/dist/types/net/core.d.ts +7 -0
  32. package/dist/types/plugin.d.ts +76 -0
  33. package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
  34. package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
  35. package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
  36. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
  37. package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
  38. package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
  39. package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
  40. package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
  41. package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
  42. package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
  43. package/dist/types/runtime/app.d.ts +9 -2
  44. package/dist/types/runtime/fetch.d.ts +2 -0
  45. package/dist/types/runtime/input.d.ts +8 -1
  46. package/dist/types/runtime/media.d.ts +6 -10
  47. package/dist/types/runtime/misc.d.ts +4 -1
  48. package/dist/types/runtime/net.d.ts +3 -2
  49. package/dist/types/runtime/touch.d.ts +32 -0
  50. package/dist/types/scene/defineScene.d.ts +43 -2
  51. package/dist/types/scene/editor.d.ts +52 -0
  52. package/dist/types/scene/gizmos.d.ts +7 -4
  53. package/dist/types/ui/NativeView.d.ts +6 -4
  54. package/dist/types/ui/UI.d.ts +1 -1
  55. package/dist/types/ui/UIBottomSheet.d.ts +6 -12
  56. package/dist/types/ui/UIButton.d.ts +12 -14
  57. package/dist/types/ui/UIContainer.d.ts +0 -6
  58. package/dist/types/ui/UIImage.d.ts +1 -4
  59. package/dist/types/ui/UIInput.d.ts +8 -24
  60. package/dist/types/ui/UIModal.d.ts +0 -2
  61. package/dist/types/ui/UINode.d.ts +70 -56
  62. package/dist/types/ui/UIPager.d.ts +28 -27
  63. package/dist/types/ui/UIPopover.d.ts +0 -2
  64. package/dist/types/ui/UIScreen.d.ts +12 -17
  65. package/dist/types/ui/UIScrollable.d.ts +1 -4
  66. package/dist/types/ui/UIText.d.ts +0 -2
  67. package/dist/types/ui/UIVideo.d.ts +3 -5
  68. package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
  69. package/dist/types/ui/UIWidget.d.ts +10 -9
  70. package/dist/types/ui/colorKeys.gen.d.ts +9 -0
  71. package/dist/types/ui/presentable.d.ts +46 -32
  72. package/dist/types/ui/router.d.ts +18 -7
  73. package/dist/types/ui/styleColor.d.ts +1 -0
  74. package/dist/types/ui/transitions.d.ts +18 -0
  75. package/dist/types/ui/tree.d.ts +75 -0
  76. package/dist/types/version.d.ts +10 -0
  77. package/dist/types.json +1 -1
  78. package/package.json +12 -3
  79. package/prompts/2d.md +2 -6
  80. package/prompts/3d.md +1 -5
  81. package/prompts/README.md +142 -142
  82. package/prompts/canvas.md +9 -8
  83. package/prompts/compose.ts +1 -1
  84. package/prompts/core.md +3 -3
  85. package/prompts/design.md +1 -1
  86. package/prompts/dist/2d-game.md +77 -54
  87. package/prompts/dist/3d-app.md +75 -52
  88. package/prompts/dist/ar-app.md +74 -51
  89. package/prompts/dist/design.md +30 -8
  90. package/prompts/dist/ui-app.md +73 -46
  91. package/prompts/ui-design.md +2 -3
  92. package/prompts/ui.md +45 -37
  93. package/src/animate/tween/Animation.ts +262 -378
  94. package/src/animate/tween/animateValue.ts +103 -100
  95. package/src/animate/tween/easing.ts +10 -3
  96. package/src/animate/tween/spec.ts +505 -479
  97. package/src/audio/Sound.ts +3 -3
  98. package/src/audio/audio.ts +162 -161
  99. package/src/bridges/2d.d.ts +317 -0
  100. package/src/bridges/app.d.ts +91 -0
  101. package/src/bridges/audio.d.ts +97 -0
  102. package/src/bridges/canvas.d.ts +79 -0
  103. package/src/bridges/device.d.ts +72 -0
  104. package/src/bridges/fetch.d.ts +80 -0
  105. package/src/bridges/files.d.ts +70 -0
  106. package/src/bridges/gl.d.ts +1133 -0
  107. package/src/bridges/input.d.ts +72 -0
  108. package/src/bridges/media.d.ts +55 -0
  109. package/src/bridges/nav.d.ts +71 -0
  110. package/src/bridges/net.d.ts +52 -0
  111. package/src/bridges/service.d.ts +52 -0
  112. package/src/bridges/socket.d.ts +31 -0
  113. package/src/bridges/storage.d.ts +33 -0
  114. package/src/bridges/tree.d.ts +301 -0
  115. package/src/bridges/types.d.ts +49 -0
  116. package/src/canvas/Canvas.ts +114 -159
  117. package/src/canvas/gen/cssColor.ts +224 -0
  118. package/src/canvas/gen/recorder.ts +212 -0
  119. package/src/canvas/gen/spec.ts +201 -0
  120. package/src/chisel.ts +193 -0
  121. package/src/compile/assetMacro.ts +1 -1
  122. package/src/compile/bundler.ts +11 -2
  123. package/src/compile/compileProject.ts +43 -4
  124. package/src/compile/fontMacro.ts +3 -4
  125. package/src/compile/header.ts +26 -5
  126. package/src/compile/index.ts +3 -1
  127. package/src/compile/liteMaterial.ts +1 -1
  128. package/src/compile/sceneEditor.ts +11 -26
  129. package/src/core/color.ts +73 -30
  130. package/src/core/pins.ts +51 -0
  131. package/src/core/signals.ts +8 -1
  132. package/src/g2/CharacterController2D.ts +3 -3
  133. package/src/g2/Node2D.ts +57 -39
  134. package/src/g2/Physics2D.ts +2 -2
  135. package/src/g2/Scene2D.ts +35 -23
  136. package/src/g2/Texture2D.ts +1 -1
  137. package/src/g2/loop.ts +4 -4
  138. package/src/gl/Foliage.ts +72 -17
  139. package/src/gl/Geometry.ts +1 -2
  140. package/src/gl/Light.ts +13 -0
  141. package/src/gl/Lightmap.ts +45 -29
  142. package/src/gl/Material.ts +95 -51
  143. package/src/gl/Model.ts +172 -167
  144. package/src/gl/Node.ts +91 -24
  145. package/src/gl/Particles.ts +20 -1
  146. package/src/gl/Scene.ts +100 -44
  147. package/src/gl/Texture.ts +8 -7
  148. package/src/gl/animation/AnimationClip.ts +1 -1
  149. package/src/gl/animation/Locomotion.ts +77 -8
  150. package/src/gl/nav/NavMesh.ts +3 -4
  151. package/src/gl/physics/Physics.ts +2 -2
  152. package/src/gl/physics/physicsEvents.ts +3 -3
  153. package/src/gl/terrain/Terrain.ts +33 -5
  154. package/src/gl/touch.ts +14 -15
  155. package/src/host.d.ts +2 -3
  156. package/src/inject.editor.ts +7 -0
  157. package/src/inject.ts +233 -236
  158. package/src/net/core.ts +6 -5
  159. package/src/net/index.ts +1 -1
  160. package/src/net/replication.ts +1 -1
  161. package/src/plugin.ts +191 -0
  162. package/src/plugins/gen/camera/contract.d.ts +27 -0
  163. package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
  164. package/src/plugins/gen/camera/sdk/camera.ts +57 -0
  165. package/src/plugins/gen/geolocation/contract.d.ts +50 -0
  166. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
  167. package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
  168. package/src/plugins/gen/map/contract.d.ts +144 -0
  169. package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
  170. package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
  171. package/src/plugins/gen/push/contract.d.ts +61 -0
  172. package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
  173. package/src/plugins/gen/push/sdk/push.ts +105 -0
  174. package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
  175. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
  176. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
  177. package/src/plugins/permission.ts +5 -4
  178. package/src/runtime/app.ts +20 -9
  179. package/src/runtime/appEvents.ts +5 -4
  180. package/src/runtime/channel.ts +18 -15
  181. package/src/runtime/clipboard.ts +4 -3
  182. package/src/runtime/datetime.ts +2 -1
  183. package/src/runtime/device.ts +17 -15
  184. package/src/runtime/fetch.ts +30 -20
  185. package/src/runtime/files.ts +16 -15
  186. package/src/runtime/input.ts +21 -8
  187. package/src/runtime/media.ts +50 -46
  188. package/src/runtime/misc.ts +7 -3
  189. package/src/runtime/net.ts +8 -7
  190. package/src/runtime/rpc.ts +1 -3
  191. package/src/runtime/service.ts +19 -14
  192. package/src/runtime/share.ts +4 -3
  193. package/src/runtime/storage.ts +6 -4
  194. package/src/runtime/touch.ts +32 -0
  195. package/src/scene/defineScene.ts +61 -365
  196. package/src/scene/editor.ts +408 -0
  197. package/src/scene/editorPlugins.ts +3 -3
  198. package/src/scene/gizmos.ts +154 -148
  199. package/src/server/db/marci/query.ts +1 -1
  200. package/src/server/host.ts +1 -1
  201. package/src/server/runtime.ts +1 -1
  202. package/src/ui/NativeView.ts +72 -25
  203. package/src/ui/UI.ts +3 -3
  204. package/src/ui/UIBottomSheet.ts +16 -17
  205. package/src/ui/UIButton.ts +54 -16
  206. package/src/ui/UIContainer.ts +0 -6
  207. package/src/ui/UIImage.ts +34 -30
  208. package/src/ui/UIInput.ts +29 -37
  209. package/src/ui/UIModal.ts +1 -3
  210. package/src/ui/UINode.ts +347 -297
  211. package/src/ui/UIPager.ts +93 -78
  212. package/src/ui/UIPopover.ts +0 -2
  213. package/src/ui/UIScreen.ts +41 -36
  214. package/src/ui/UIScrollable.ts +19 -13
  215. package/src/ui/UISpacer.ts +1 -1
  216. package/src/ui/UITabs.ts +8 -6
  217. package/src/ui/UIText.ts +7 -17
  218. package/src/ui/UIVideo.ts +30 -27
  219. package/src/ui/UIVirtualizedList.ts +58 -59
  220. package/src/ui/UIWidget.ts +38 -18
  221. package/src/ui/colorKeys.gen.ts +37 -0
  222. package/src/ui/fonts.ts +2 -2
  223. package/src/ui/presentable.ts +59 -42
  224. package/src/ui/router.ts +52 -35
  225. package/src/ui/styleColor.ts +56 -0
  226. package/src/ui/theme.ts +7 -6
  227. package/src/ui/transitions.ts +249 -0
  228. package/src/ui/tree.ts +346 -0
  229. package/src/version.ts +24 -0
  230. package/tests/helpers/engineWorld.ts +11 -0
  231. package/tests/helpers/fakeTree.ts +353 -0
  232. package/tests/helpers/hostStubs.ts +31 -0
  233. package/tests/helpers/index.ts +14 -0
  234. package/tests/helpers/memoryMarci.ts +124 -0
  235. package/tests/helpers/phases.ts +23 -0
  236. package/tests/helpers/preload.ts +18 -0
  237. package/tests/helpers/stubApp.ts +2 -0
  238. package/tests/helpers/stubDevice.ts +2 -0
  239. package/tests/helpers/stubFetch.ts +2 -0
  240. package/tests/helpers/stubInput.ts +2 -0
  241. package/dist/inject.js +0 -4730
  242. package/dist/types/core/registry.d.ts +0 -7
  243. package/dist/types/plugins/camera.d.ts +0 -25
  244. package/dist/types/plugins/push.d.ts +0 -46
  245. package/src/bridges.d.ts +0 -1760
  246. package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
  247. package/src/compile/__tests__/assetMacro.test.ts +0 -100
  248. package/src/compile/__tests__/assetName.test.ts +0 -55
  249. package/src/compile/__tests__/compile.test.ts +0 -310
  250. package/src/compile/__tests__/detectEntry.test.ts +0 -151
  251. package/src/compile/__tests__/fontMacro.test.ts +0 -199
  252. package/src/compile/__tests__/serverSplit.test.ts +0 -27
  253. package/src/core/__tests__/stateMachine.test.ts +0 -132
  254. package/src/core/registry.ts +0 -23
  255. package/src/plugins/camera.ts +0 -81
  256. package/src/plugins/push.ts +0 -132
  257. package/src/plugins/qr.ts +0 -73
package/package.json CHANGED
@@ -8,12 +8,20 @@
8
8
  "./compile/detectEntry": "./src/compile/detectEntry.ts",
9
9
  "./compile/shaderSchema": "./src/compile/shaderSchema.ts",
10
10
  "./compile/liteMaterial": "./src/compile/liteMaterial.ts",
11
+ "./color": "./src/core/color.ts",
11
12
  "./inject": "./src/inject.ts",
13
+ "./plugin": "./src/plugin.ts",
12
14
  "./server": "./src/server/host.ts",
13
15
  "./prompts/select": "./prompts/select.ts",
14
16
  "./global.d.ts": "./dist/global.d.ts",
15
17
  "./host.d.ts": "./dist/host.d.ts",
16
- "./types.json": "./dist/types.json"
18
+ "./editor.d.ts": "./dist/editor.d.ts",
19
+ "./types.json": "./dist/types.json",
20
+ "./bridges/*": "./src/bridges/*.d.ts",
21
+ "./ui/tree": "./src/ui/tree.ts",
22
+ "./testing": "./tests/helpers/index.ts",
23
+ "./testing/preload": "./tests/helpers/preload.ts",
24
+ "./version": "./src/version.ts"
17
25
  },
18
26
  "scripts": {
19
27
  "build": "bun run build-types.ts",
@@ -25,7 +33,7 @@
25
33
  "prepublishOnly": "bun run build"
26
34
  },
27
35
  "dependencies": {
28
- "@letary/chisel": "^0.10.0"
36
+ "@letary/chisel": "^0.13.0"
29
37
  },
30
38
  "devDependencies": {
31
39
  "@types/bun": "^1.3.14",
@@ -33,11 +41,12 @@
33
41
  "typescript": "~5.8.3",
34
42
  "gl-matrix": "^3.4.4"
35
43
  },
36
- "version": "1.1.0",
44
+ "version": "2.0.0",
37
45
  "files": [
38
46
  "src",
39
47
  "dist",
40
48
  "prompts",
49
+ "tests/helpers",
41
50
  "README.md"
42
51
  ],
43
52
  "publishConfig": {
package/prompts/2d.md CHANGED
@@ -5,7 +5,7 @@ Scene2D + nodes (Sprite, Tilemap) + aspects (SpriteAnimation, Shape2D/Physics2D/
5
5
  // ===== SCENE & NODES =====
6
6
 
7
7
  new Scene2D(options?: {
8
- background?: Color // clear color (hex string or 0xRRGGBB); settable later via scene.background
8
+ background?: Color // clear color (any CSS color or 0xRRGGBB); settable later via scene.background
9
9
  filter?: 'nearest' | 'linear' // texture sampling; default nearest (crisp pixel art), 'linear' for hi-res
10
10
  }) // art. filter is GLOBAL (one sampler for all textures), re-applied on open()
11
11
 
@@ -63,7 +63,7 @@ new Sprite(options?: {
63
63
  // [0.5, 1] = bottom-center = "feet" — use on Y-sorted layers
64
64
  size?: Vec2Like // world size; default = texture pixel dims (1 texel = 1 world unit)
65
65
  frame?: [u0, v0, u1, v1] // normalized UV sub-rect
66
- color?: Color // tint MULTIPLIED with the texture; hex/packed only (default white = untinted)
66
+ color?: Color // tint MULTIPLIED with the texture (default white = untinted)
67
67
  opacity?: number // 0..1
68
68
  layer?: number
69
69
  position?: Vec2Like
@@ -259,10 +259,6 @@ sprite.aspect(Shape2D, { box: [12, 20] }) // ✅ declare the extents you want
259
259
  setLoop(() => sprite.setFramePx(...)) // WRONG
260
260
  sprite.aspect(SpriteAnimation, { size: [32, 48], clips: { walk: [0,1,2,3] } }) // ✅ then anim.play('walk')
261
261
 
262
- // ❌ CSS color strings on sprites — engine colors are hex/packed only (Canvas fillStyle is the exception)
263
- sprite.color = 'rgba(255,0,0,0.5)' // WRONG
264
- sprite.color = '#ff0000'; sprite.opacity = 0.5 // ✅
265
-
266
262
  // ❌ attaching SpriteAnimation before the sprite has a texture — throws (the grid slices from it)
267
263
  // ❌ attaching Physics2D before Shape2D on the same node — throws (attach the shape first)
268
264
  // ❌ setting spawn position AFTER attaching Physics2D — the body already seeded from [0,0]; position first
package/prompts/3d.md CHANGED
@@ -86,7 +86,7 @@ material.map = tex // Texture | Canvas | null
86
86
 
87
87
  // Custom compiled shaders (.mat files) — load, then set uniforms by name (chainable):
88
88
  const mat = new Material(await fetch(asset('./hologram.mat'), { useOnce: true }))
89
- .set('baseColor', '#ffa200') // '#rrggbb' / '#rrggbbaa' hex STRINGS only
89
+ .set('baseColor', '#ffa200') // color STRINGS only (any CSS color); a number is a float
90
90
  .set('roughness', 0.54) // number → float, boolean → bool
91
91
  .set('glowMap', tex) // Texture → sampler
92
92
  .set('dir', [0, 1, 0]) // number[] / Float32Array → vector. NOT a Vec3 — spread it: [...v]
@@ -294,10 +294,6 @@ mat.set('lightDir', dir)
294
294
  Light.point(...)
295
295
  // ✅ Light.sun() + scene IBL is the whole lighting model; fake glows with unlit/bloom/particles
296
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
297
  // ❌ a fresh Material per frame (or per particle) to animate a look
302
298
  setLoop(() => { mesh.material = Material.lit({ color: next() }) })
303
299
  // ✅ mutate the one material: mesh.material.color = next() / mat.set('progress', t)
package/prompts/README.md CHANGED
@@ -1,142 +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/init/templates.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.
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/init/templates.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, colors are CSS strings / 0xRRGGBB / [r,g,b(,a)] everywhere, `animate` can't
141
+ tween strings/colors, `node.physics` (not `.body`), no `Camera2D.follow`, no
142
+ `UIScrollable.scrollTo`, models can't be cloned.
package/prompts/canvas.md CHANGED
@@ -8,20 +8,21 @@ const c = new Canvas(width, height, { pixelRatio: device.pixelRatio })
8
8
  // width/height are LOGICAL units — author all drawing logical; pixelRatio only multiplies baked
9
9
  // resolution (crispness), never the on-screen size.
10
10
 
11
- c.fillStyle = '#ff8800' // CSS color STRINGS work here (the exception to engine hex-only colors);
12
- c.lineCap = 'round' // also strokeStyle, lineWidth, lineJoin, globalAlpha,
11
+ c.fillStyle = '#ff8800' // any CSS color string, as everywhere in the SDK;
12
+ c.lineCap = 'round' // also strokeStyle, lineWidth, lineJoin, miterLimit, globalAlpha, letterSpacing,
13
+ c.setLineDash([4, 2]) // dashes; a gradient: c.fillStyle = c.createLinearGradient(x0,y0,x1,y1).addColorStop(0,'#fff').addColorStop(1,'transparent')
13
14
  c.font = 'bold 16px sans-serif' // textAlign ('left'|'center'|'right'), textBaseline
14
15
  // custom fonts: draw only AFTER await registerFont(...)
15
16
  c.fillRect(x, y, w, h) / c.strokeRect / c.clearRect
16
- c.beginPath().moveTo(x,y).lineTo(x,y).arc(x,y,r,a0,a1).rect(x,y,w,h).roundRect(x,y,w,h,r).closePath() // all chain
17
- c.fill() / c.stroke() / c.fillText(text, x, y) / c.strokeText(text, x, y)
18
- c.measureText(text): { width, ascent, descent } // in the CURRENT font, logical px
19
- c.drawImage(bmp: Bitmap, dx, dy [, dw, dh]) // blits a Bitmap only (from c.toBitmap()), nothing else
17
+ c.beginPath().moveTo(x,y).lineTo(x,y).arc(x,y,r,a0,a1).arcTo(x1,y1,x2,y2,r).ellipse(x,y,rx,ry,rot,a0,a1).rect(x,y,w,h).roundRect(x,y,w,h,r).closePath() // all chain
18
+ c.fill('evenodd'?) / c.stroke() / c.clip() / c.fillText(text, x, y) / c.strokeText(text, x, y)
19
+ c.measureText(text): { width, ascent, descent } // in the CURRENT font and letterSpacing, logical px
20
+ c.drawImage(bmp: Bitmap, dx, dy [, dw, dh]) // blits a Bitmap only (c.toBitmap(), await c.loadImage(SvgSource(svg) | fetchResponse))
20
21
 
21
22
  c.update(): this // after redrawing, push new pixels to EVERY texture/image this canvas produced
22
23
  c.reset(): this // discard the recorded drawing to start over
23
24
  c.resize(w, h): this // new logical size (takes effect next bake; measure-then-resize works pre-bake)
24
25
  c.toBitmap(): Bitmap // rasterized snapshot, usable with drawImage
25
- // GOTCHA: reset() clears the recorded STATE too — font/fillStyle/textAlign fall back to defaults on
26
- // the next bake (getters still report old values). Re-set font & friends after every reset().
26
+ // GOTCHA: reset() clears the recorded STATE too — font/fillStyle/textAlign fall back to defaults
27
+ // (the getters report the defaults again). Re-set font & friends after every reset().
27
28
  // Ever-growing drawings: flatten — const snap = c.toBitmap(); c.reset(); c.drawImage(snap, 0, 0)
@@ -1,4 +1,4 @@
1
- // Compose the prompt bundles from the modules. Run from packages/sdk:
1
+ // Compose the prompt bundles from the modules. Run from sdk:
2
2
  // bun run prompts/compose.ts
3
3
  // Writes prompts/dist/<bundle>.md and prints token-ish size stats.
4
4
 
package/prompts/core.md CHANGED
@@ -202,9 +202,9 @@ Mathf.clamp(v, min, max) / .lerp(a, b, t) / .remap(v, inMin, inMax, outMin, outM
202
202
  Mathf.damp(a, b, lambda, dt) / .moveTowards(a, b, maxDelta)
203
203
  Mathf.random(min, max) / .randomInt(min, max) // randomInt inclusive both ends
204
204
 
205
- // Colors: hex string "#e33" | "#ff3333" | "#ff3333cc" or packed int 0xff3333.
206
- // rgba(...) / named colors DON'T work in 2D/3D APIs (silently become black). UI styles are the
207
- // exception — they also accept rgb()/rgba() and basic names ("white", "black", "transparent", …).
205
+ // Colors (every API, UI and engine alike): any CSS color string ("#e33", "#ff3333cc", "rgb(255 0 0 / 50%)",
206
+ // "hsl(0 100% 50%)", "red" — the CSS names, green = #008000), packed int 0xff3333 (opaque 0xRRGGBB),
207
+ // or [r, g, b(, a)] 0..1. A non-color throws. var(--x) only in UI styles.
208
208
 
209
209
  // ===== NETWORK =====
210
210
 
package/prompts/design.md CHANGED
@@ -50,7 +50,7 @@ import { colors, spacing, radius } from "./tokens"
50
50
  export const PrimaryButton = (label: string, o: { name?: string, disabled?: boolean } = {}) =>
51
51
  UIButton(UIText(label).style({ fontSize: 16, fontWeight: 700, color: o.disabled ? colors.muted : colors.onAccent }))
52
52
  .style({ height: 52, borderRadius: radius.md, bgColor: o.disabled ? colors.accentDeep : colors.accent,
53
- justifyContent: "center", alignItems: "center", name: o.name, onPressed: { bgColor: colors.accentPressed } })
53
+ justifyContent: "center", alignItems: "center", name: o.name, $pressed: { bgColor: colors.accentPressed } })
54
54
 
55
55
  export const SectionLabel = (t: string) =>
56
56
  UIText(t.toUpperCase()).style({ fontSize: 11, fontWeight: 600, letterSpacing: 2, color: colors.faint })