lecodes-sdk 1.2.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 (260) 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 +8 -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/Scene.d.ts +41 -13
  25. package/dist/types/gl/Texture.d.ts +1 -1
  26. package/dist/types/gl/animation/Locomotion.d.ts +8 -1
  27. package/dist/types/inject.d.ts +11 -11
  28. package/dist/types/inject.editor.d.ts +1 -0
  29. package/dist/types/net/core.d.ts +7 -0
  30. package/dist/types/plugin.d.ts +76 -0
  31. package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
  32. package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
  33. package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
  34. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
  35. package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
  36. package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
  37. package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
  38. package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
  39. package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
  40. package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
  41. package/dist/types/runtime/app.d.ts +9 -2
  42. package/dist/types/runtime/fetch.d.ts +2 -0
  43. package/dist/types/runtime/input.d.ts +1 -1
  44. package/dist/types/runtime/media.d.ts +6 -10
  45. package/dist/types/runtime/misc.d.ts +4 -1
  46. package/dist/types/runtime/net.d.ts +3 -2
  47. package/dist/types/runtime/touch.d.ts +32 -0
  48. package/dist/types/scene/defineScene.d.ts +43 -2
  49. package/dist/types/scene/editor.d.ts +52 -0
  50. package/dist/types/scene/gizmos.d.ts +7 -4
  51. package/dist/types/ui/NativeView.d.ts +6 -4
  52. package/dist/types/ui/UI.d.ts +1 -1
  53. package/dist/types/ui/UIBottomSheet.d.ts +6 -12
  54. package/dist/types/ui/UIButton.d.ts +12 -14
  55. package/dist/types/ui/UIContainer.d.ts +0 -6
  56. package/dist/types/ui/UIImage.d.ts +1 -4
  57. package/dist/types/ui/UIInput.d.ts +8 -24
  58. package/dist/types/ui/UIModal.d.ts +0 -2
  59. package/dist/types/ui/UINode.d.ts +70 -56
  60. package/dist/types/ui/UIPager.d.ts +28 -27
  61. package/dist/types/ui/UIPopover.d.ts +0 -2
  62. package/dist/types/ui/UIScreen.d.ts +12 -17
  63. package/dist/types/ui/UIScrollable.d.ts +1 -4
  64. package/dist/types/ui/UIText.d.ts +0 -2
  65. package/dist/types/ui/UIVideo.d.ts +3 -5
  66. package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
  67. package/dist/types/ui/UIWidget.d.ts +10 -9
  68. package/dist/types/ui/colorKeys.gen.d.ts +9 -0
  69. package/dist/types/ui/presentable.d.ts +46 -32
  70. package/dist/types/ui/router.d.ts +18 -7
  71. package/dist/types/ui/styleColor.d.ts +1 -0
  72. package/dist/types/ui/transitions.d.ts +18 -0
  73. package/dist/types/ui/tree.d.ts +75 -0
  74. package/dist/types/version.d.ts +10 -0
  75. package/dist/types.json +1 -1
  76. package/package.json +12 -3
  77. package/prompts/2d.md +2 -6
  78. package/prompts/3d.md +1 -5
  79. package/prompts/README.md +1 -1
  80. package/prompts/canvas.md +9 -8
  81. package/prompts/compose.ts +1 -1
  82. package/prompts/core.md +3 -3
  83. package/prompts/design.md +1 -1
  84. package/prompts/dist/2d-game.md +473 -239
  85. package/prompts/dist/3d-app.md +553 -205
  86. package/prompts/dist/ar-app.md +435 -202
  87. package/prompts/dist/design.md +113 -95
  88. package/prompts/dist/ui-app.md +386 -170
  89. package/prompts/ui-design.md +2 -3
  90. package/prompts/ui.md +45 -37
  91. package/src/animate/tween/Animation.ts +34 -150
  92. package/src/animate/tween/Timeline.ts +175 -175
  93. package/src/animate/tween/animateValue.ts +6 -3
  94. package/src/animate/tween/easing.ts +10 -3
  95. package/src/animate/tween/spec.ts +41 -15
  96. package/src/audio/Sound.ts +3 -3
  97. package/src/audio/audio.ts +2 -1
  98. package/src/bridges/2d.d.ts +317 -0
  99. package/src/bridges/app.d.ts +91 -0
  100. package/src/bridges/audio.d.ts +97 -0
  101. package/src/bridges/canvas.d.ts +79 -0
  102. package/src/bridges/device.d.ts +72 -0
  103. package/src/bridges/fetch.d.ts +80 -0
  104. package/src/bridges/files.d.ts +70 -0
  105. package/src/bridges/gl.d.ts +1133 -0
  106. package/src/bridges/input.d.ts +72 -0
  107. package/src/bridges/media.d.ts +55 -0
  108. package/src/bridges/nav.d.ts +71 -0
  109. package/src/bridges/net.d.ts +52 -0
  110. package/src/bridges/service.d.ts +52 -0
  111. package/src/bridges/socket.d.ts +31 -0
  112. package/src/bridges/storage.d.ts +33 -0
  113. package/src/bridges/tree.d.ts +301 -0
  114. package/src/bridges/types.d.ts +49 -0
  115. package/src/canvas/Canvas.ts +114 -159
  116. package/src/canvas/gen/cssColor.ts +224 -0
  117. package/src/canvas/gen/recorder.ts +212 -0
  118. package/src/canvas/gen/spec.ts +201 -0
  119. package/src/chisel.ts +193 -0
  120. package/src/compile/assetMacro.ts +1 -1
  121. package/src/compile/bundler.ts +11 -2
  122. package/src/compile/compileProject.ts +43 -4
  123. package/src/compile/fontMacro.ts +3 -4
  124. package/src/compile/header.ts +26 -5
  125. package/src/compile/index.ts +3 -1
  126. package/src/compile/liteMaterial.ts +1 -1
  127. package/src/compile/sceneEditor.ts +11 -26
  128. package/src/core/color.ts +73 -30
  129. package/src/core/pins.ts +51 -0
  130. package/src/core/signals.ts +8 -1
  131. package/src/g2/CharacterController2D.ts +3 -3
  132. package/src/g2/Node2D.ts +57 -39
  133. package/src/g2/Physics2D.ts +2 -2
  134. package/src/g2/Scene2D.ts +35 -23
  135. package/src/g2/Texture2D.ts +1 -1
  136. package/src/g2/loop.ts +4 -4
  137. package/src/gl/CameraPlace.ts +52 -52
  138. package/src/gl/Foliage.ts +72 -17
  139. package/src/gl/Geometry.ts +1 -2
  140. package/src/gl/Light.ts +10 -0
  141. package/src/gl/Lightmap.ts +45 -29
  142. package/src/gl/Material.ts +95 -51
  143. package/src/gl/Mesh.ts +120 -120
  144. package/src/gl/Model.ts +21 -16
  145. package/src/gl/Node.ts +91 -24
  146. package/src/gl/Particles.ts +1 -1
  147. package/src/gl/Scene.ts +100 -44
  148. package/src/gl/Texture.ts +8 -7
  149. package/src/gl/animation/AnimationClip.ts +1 -1
  150. package/src/gl/animation/DynamicBone.ts +482 -482
  151. package/src/gl/animation/Locomotion.ts +8 -3
  152. package/src/gl/nav/NavMesh.ts +3 -4
  153. package/src/gl/physics/Physics.ts +2 -2
  154. package/src/gl/physics/physicsEvents.ts +3 -3
  155. package/src/gl/scenarios.ts +291 -291
  156. package/src/gl/terrain/Terrain.ts +4 -5
  157. package/src/gl/touch.ts +14 -15
  158. package/src/host.d.ts +2 -3
  159. package/src/inject.editor.ts +7 -0
  160. package/src/inject.ts +13 -16
  161. package/src/net/core.ts +6 -5
  162. package/src/net/index.ts +1 -1
  163. package/src/net/replication.ts +1 -1
  164. package/src/plugin.ts +191 -0
  165. package/src/plugins/gen/camera/contract.d.ts +27 -0
  166. package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
  167. package/src/plugins/gen/camera/sdk/camera.ts +57 -0
  168. package/src/plugins/gen/geolocation/contract.d.ts +50 -0
  169. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
  170. package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
  171. package/src/plugins/gen/map/contract.d.ts +144 -0
  172. package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
  173. package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
  174. package/src/plugins/gen/push/contract.d.ts +61 -0
  175. package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
  176. package/src/plugins/gen/push/sdk/push.ts +105 -0
  177. package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
  178. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
  179. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
  180. package/src/plugins/permission.ts +5 -4
  181. package/src/runtime/app.ts +20 -9
  182. package/src/runtime/appEvents.ts +5 -4
  183. package/src/runtime/channel.ts +18 -15
  184. package/src/runtime/clipboard.ts +4 -3
  185. package/src/runtime/datetime.ts +2 -1
  186. package/src/runtime/device.ts +17 -15
  187. package/src/runtime/fetch.ts +30 -20
  188. package/src/runtime/files.ts +16 -15
  189. package/src/runtime/input.ts +12 -10
  190. package/src/runtime/media.ts +50 -46
  191. package/src/runtime/misc.ts +7 -3
  192. package/src/runtime/net.ts +8 -7
  193. package/src/runtime/rpc.ts +1 -3
  194. package/src/runtime/service.ts +19 -14
  195. package/src/runtime/share.ts +4 -3
  196. package/src/runtime/storage.ts +6 -4
  197. package/src/runtime/touch.ts +32 -0
  198. package/src/scene/defineScene.ts +61 -365
  199. package/src/scene/editor.ts +408 -0
  200. package/src/scene/editorPlugins.ts +3 -3
  201. package/src/scene/gizmos.ts +15 -9
  202. package/src/server/db/marci/query.ts +1 -1
  203. package/src/server/host.ts +1 -1
  204. package/src/server/runtime.ts +1 -1
  205. package/src/ui/NativeView.ts +72 -25
  206. package/src/ui/UI.ts +3 -3
  207. package/src/ui/UIBottomSheet.ts +16 -17
  208. package/src/ui/UIButton.ts +54 -16
  209. package/src/ui/UIContainer.ts +0 -6
  210. package/src/ui/UIImage.ts +34 -30
  211. package/src/ui/UIInput.ts +29 -37
  212. package/src/ui/UIModal.ts +1 -3
  213. package/src/ui/UINode.ts +347 -297
  214. package/src/ui/UIPager.ts +93 -78
  215. package/src/ui/UIPopover.ts +0 -2
  216. package/src/ui/UIScreen.ts +41 -36
  217. package/src/ui/UIScrollable.ts +19 -13
  218. package/src/ui/UISpacer.ts +1 -1
  219. package/src/ui/UITabs.ts +8 -6
  220. package/src/ui/UIText.ts +7 -17
  221. package/src/ui/UIVideo.ts +30 -27
  222. package/src/ui/UIVirtualizedList.ts +58 -59
  223. package/src/ui/UIWidget.ts +38 -18
  224. package/src/ui/colorKeys.gen.ts +37 -0
  225. package/src/ui/fonts.ts +2 -2
  226. package/src/ui/presentable.ts +59 -42
  227. package/src/ui/router.ts +52 -35
  228. package/src/ui/styleColor.ts +56 -0
  229. package/src/ui/theme.ts +7 -6
  230. package/src/ui/transitions.ts +249 -0
  231. package/src/ui/tree.ts +346 -0
  232. package/src/version.ts +24 -0
  233. package/tests/helpers/engineWorld.ts +11 -0
  234. package/tests/helpers/fakeTree.ts +353 -0
  235. package/tests/helpers/hostStubs.ts +31 -0
  236. package/tests/helpers/index.ts +14 -0
  237. package/tests/helpers/memoryMarci.ts +124 -0
  238. package/tests/helpers/phases.ts +23 -0
  239. package/tests/helpers/preload.ts +18 -0
  240. package/tests/helpers/stubApp.ts +2 -0
  241. package/tests/helpers/stubDevice.ts +2 -0
  242. package/tests/helpers/stubFetch.ts +2 -0
  243. package/tests/helpers/stubInput.ts +2 -0
  244. package/dist/inject.js +0 -4629
  245. package/dist/types/core/registry.d.ts +0 -7
  246. package/dist/types/plugins/camera.d.ts +0 -25
  247. package/dist/types/plugins/push.d.ts +0 -46
  248. package/src/bridges.d.ts +0 -1769
  249. package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
  250. package/src/compile/__tests__/assetMacro.test.ts +0 -100
  251. package/src/compile/__tests__/assetName.test.ts +0 -55
  252. package/src/compile/__tests__/compile.test.ts +0 -310
  253. package/src/compile/__tests__/detectEntry.test.ts +0 -151
  254. package/src/compile/__tests__/fontMacro.test.ts +0 -199
  255. package/src/compile/__tests__/serverSplit.test.ts +0 -27
  256. package/src/core/__tests__/stateMachine.test.ts +0 -132
  257. package/src/core/registry.ts +0 -23
  258. package/src/plugins/camera.ts +0 -81
  259. package/src/plugins/push.ts +0 -132
  260. package/src/plugins/qr.ts +0 -73
@@ -0,0 +1,212 @@
1
+ // GENERATED by scripts/vendor-canvas.ts from engines/canvas/recorders/ts/src/recorder.ts @ ae6bc30b952b — do not edit here.
2
+ // Bump the submodule and run `bun run vendor:canvas`; `bun run check:canvas-ops` fails when this copy is stale.
3
+ // The TypeScript recorder: a Canvas2D-shaped API whose calls WRITE the opcode stream of spec/ops.h.
4
+ // Nothing is drawn here — the stream goes to the core interpreter (native, or wasm on the web), which
5
+ // turns it into a draw list a painter replays. An app-facing Canvas class wraps a Recorder and adds
6
+ // what needs a painter (measureText, surfaces, textures).
7
+ //
8
+ // Differences from the browser, on purpose:
9
+ // - colors and fonts are CSS strings resolved by the core (an invalid one keeps the previous style)
10
+ // - a Gradient is serialized when it is ASSIGNED to fillStyle / strokeStyle; stops added later
11
+ // do not affect an assignment already made
12
+ // - drawImage takes a surface id (an ImageRef with its device size for the short forms)
13
+ // - every drawing call returns `this`, so drawing chains
14
+ import { OP, LineJoin, LineCap, TextAlign, TextBaseline, FillRule, Gradient as GradientKind } from "./spec"
15
+
16
+ export type FillRuleName = "nonzero" | "evenodd"
17
+ export type LineJoinName = "miter" | "round" | "bevel"
18
+ export type LineCapName = "butt" | "round" | "square"
19
+ export type TextAlignName = "left" | "center" | "right" | "start" | "end"
20
+ export type TextBaselineName = "alphabetic" | "top" | "middle" | "bottom" | "hanging" | "ideographic"
21
+
22
+ const JOIN: Record<LineJoinName, number> = { miter: LineJoin.MITER, round: LineJoin.ROUND, bevel: LineJoin.BEVEL }
23
+ const CAP: Record<LineCapName, number> = { butt: LineCap.BUTT, round: LineCap.ROUND, square: LineCap.SQUARE }
24
+ const ALIGN: Record<TextAlignName, number> = { left: TextAlign.LEFT, center: TextAlign.CENTER, right: TextAlign.RIGHT, start: TextAlign.START, end: TextAlign.END }
25
+ const BASELINE: Record<TextBaselineName, number> = {
26
+ alphabetic: TextBaseline.ALPHABETIC, top: TextBaseline.TOP, middle: TextBaseline.MIDDLE,
27
+ bottom: TextBaseline.BOTTOM, hanging: TextBaseline.HANGING, ideographic: TextBaseline.IDEOGRAPHIC,
28
+ }
29
+ const RULE: Record<FillRuleName, number> = { nonzero: FillRule.NONZERO, evenodd: FillRule.EVENODD }
30
+ // The shadowed style fields save() / restore() snapshot, as the core's state stack does.
31
+ const SHADOWED = ["_globalAlpha", "_fillStyle", "_strokeStyle", "_lineWidth", "_lineJoin", "_lineCap", "_miterLimit",
32
+ "_lineDash", "_lineDashOffset", "_font", "_textAlign", "_textBaseline", "_letterSpacing"] as const
33
+
34
+ /** A gradient built by createLinearGradient / createRadialGradient; assign it to fillStyle / strokeStyle. */
35
+ export class Gradient {
36
+ readonly stops: { offset: number, color: string }[] = []
37
+ readonly kind: number
38
+ readonly coords: [number, number, number, number, number, number]
39
+ /** @internal */
40
+ constructor(kind: number, coords: [number, number, number, number, number, number]) { this.kind = kind; this.coords = coords }
41
+ addColorStop(offset: number, color: string): this {
42
+ if (!(offset >= 0 && offset <= 1)) throw new RangeError(`gradient stop offset ${offset} is outside 0..1`)
43
+ this.stops.push({ offset, color })
44
+ return this
45
+ }
46
+ }
47
+
48
+ /** The recorded stream: the f32 words and the string table the core reads. */
49
+ export interface Stream { cmd: Float32Array, refs: string[] }
50
+
51
+ /** A surface with its device size, for the short drawImage forms. */
52
+ export interface ImageRef { surface: number, width: number, height: number }
53
+
54
+ export class Recorder {
55
+ private cmd: number[] = []
56
+ private refs: string[] = []
57
+ private refIndex = new Map<string, number>()
58
+
59
+ // Shadowed state so the getters read back what was set (the stream itself is write-only).
60
+ private _globalAlpha = 1
61
+ private _fillStyle: string | Gradient = "#000000"
62
+ private _strokeStyle: string | Gradient = "#000000"
63
+ private _lineWidth = 1
64
+ private _lineJoin: LineJoinName = "miter"
65
+ private _lineCap: LineCapName = "butt"
66
+ private _miterLimit = 10
67
+ private _lineDash: number[] = []
68
+ private _lineDashOffset = 0
69
+ private _font = "10px sans-serif"
70
+ private _textAlign: TextAlignName = "start"
71
+ private _textBaseline: TextBaselineName = "alphabetic"
72
+ private _letterSpacing = 0
73
+ // One snapshot of the shadowed fields per open save(), so the getters read back what restore()
74
+ // brings back (by reference: setLineDash stores a fresh array, a Gradient is the assigned object).
75
+ private _saved: Record<string, unknown>[] = []
76
+
77
+ // ---- the stream -----------------------------------------------------------------------------
78
+
79
+ /** The words recorded so far (a copy) + the string table. */
80
+ stream(): Stream { return { cmd: new Float32Array(this.cmd), refs: this.refs.slice() } }
81
+ /** Plain arrays, for a JSON file (the golden tests' input). */
82
+ toJSON(): { cmd: number[], refs: string[] } { return { cmd: this.cmd.slice(), refs: this.refs.slice() } }
83
+ /** The number of words recorded. */
84
+ get length(): number { return this.cmd.length }
85
+ /** Discard the recording; the shadowed state is reset too (a fresh replay starts from defaults). */
86
+ reset(): this {
87
+ this.cmd.length = 0
88
+ this.refs.length = 0
89
+ this.refIndex.clear()
90
+ this._globalAlpha = 1; this._fillStyle = "#000000"; this._strokeStyle = "#000000"
91
+ this._lineWidth = 1; this._lineJoin = "miter"; this._lineCap = "butt"; this._miterLimit = 10
92
+ this._lineDash = []; this._lineDashOffset = 0
93
+ this._font = "10px sans-serif"; this._textAlign = "start"; this._textBaseline = "alphabetic"; this._letterSpacing = 0
94
+ this._saved.length = 0
95
+ return this
96
+ }
97
+
98
+ private ref(s: string): number {
99
+ let i = this.refIndex.get(s)
100
+ if (i === undefined) { i = this.refs.length; this.refs.push(s); this.refIndex.set(s, i) }
101
+ return i
102
+ }
103
+ private push(...words: number[]): this { for (const w of words) this.cmd.push(w); return this }
104
+
105
+ // ---- state stack + transforms ---------------------------------------------------------------
106
+
107
+ save(): this {
108
+ const s: Record<string, unknown> = {}
109
+ for (const k of SHADOWED) s[k] = this[k]
110
+ this._saved.push(s)
111
+ return this.push(OP.SAVE)
112
+ }
113
+ restore(): this {
114
+ const s = this._saved.pop() // an unbalanced restore() keeps the state, like the core
115
+ if (s) Object.assign(this, s)
116
+ return this.push(OP.RESTORE)
117
+ }
118
+ translate(x: number, y: number): this { return this.push(OP.TRANSLATE, x, y) }
119
+ scale(sx: number, sy: number): this { return this.push(OP.SCALE, sx, sy) }
120
+ rotate(rad: number): this { return this.push(OP.ROTATE, rad) }
121
+ transform(a: number, b: number, c: number, d: number, e: number, f: number): this { return this.push(OP.TRANSFORM, a, b, c, d, e, f) }
122
+ setTransform(a: number, b: number, c: number, d: number, e: number, f: number): this { return this.push(OP.SET_TRANSFORM, a, b, c, d, e, f) }
123
+ resetTransform(): this { return this.push(OP.RESET_TRANSFORM) }
124
+
125
+ // ---- styles ---------------------------------------------------------------------------------
126
+
127
+ get globalAlpha(): number { return this._globalAlpha }
128
+ set globalAlpha(a: number) { this._globalAlpha = a; this.push(OP.GLOBAL_ALPHA, a) }
129
+
130
+ get fillStyle(): string | Gradient { return this._fillStyle }
131
+ set fillStyle(v: string | Gradient) { this._fillStyle = v; this.style(OP.FILL_STYLE, OP.FILL_GRADIENT, v) }
132
+ get strokeStyle(): string | Gradient { return this._strokeStyle }
133
+ set strokeStyle(v: string | Gradient) { this._strokeStyle = v; this.style(OP.STROKE_STYLE, OP.STROKE_GRADIENT, v) }
134
+ private style(colorOp: number, gradientOp: number, v: string | Gradient): void {
135
+ if (typeof v === "string") { this.push(colorOp, this.ref(v)); return }
136
+ this.push(gradientOp, v.kind, ...v.coords, v.stops.length)
137
+ for (const s of v.stops) this.push(s.offset, this.ref(s.color))
138
+ }
139
+
140
+ createLinearGradient(x0: number, y0: number, x1: number, y1: number): Gradient {
141
+ return new Gradient(GradientKind.LINEAR, [x0, y0, x1, y1, 0, 0])
142
+ }
143
+ createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): Gradient {
144
+ return new Gradient(GradientKind.RADIAL, [x0, y0, x1, y1, r0, r1])
145
+ }
146
+
147
+ get lineWidth(): number { return this._lineWidth }
148
+ set lineWidth(w: number) { this._lineWidth = w; this.push(OP.LINE_WIDTH, w) }
149
+ get lineJoin(): LineJoinName { return this._lineJoin }
150
+ set lineJoin(j: LineJoinName) { this._lineJoin = j; this.push(OP.LINE_JOIN, JOIN[j]) }
151
+ get lineCap(): LineCapName { return this._lineCap }
152
+ set lineCap(c: LineCapName) { this._lineCap = c; this.push(OP.LINE_CAP, CAP[c]) }
153
+ get miterLimit(): number { return this._miterLimit }
154
+ set miterLimit(m: number) { this._miterLimit = m; this.push(OP.MITER_LIMIT, m) }
155
+ setLineDash(segments: number[]): this {
156
+ this._lineDash = segments.slice()
157
+ return this.push(OP.LINE_DASH, segments.length, ...segments)
158
+ }
159
+ getLineDash(): number[] { return this._lineDash.slice() }
160
+ get lineDashOffset(): number { return this._lineDashOffset }
161
+ set lineDashOffset(o: number) { this._lineDashOffset = o; this.push(OP.LINE_DASH_OFFSET, o) }
162
+
163
+ get font(): string { return this._font }
164
+ set font(f: string) { this._font = f; this.push(OP.FONT, this.ref(f)) }
165
+ get textAlign(): TextAlignName { return this._textAlign }
166
+ set textAlign(a: TextAlignName) { this._textAlign = a; this.push(OP.TEXT_ALIGN, ALIGN[a]) }
167
+ get textBaseline(): TextBaselineName { return this._textBaseline }
168
+ set textBaseline(b: TextBaselineName) { this._textBaseline = b; this.push(OP.TEXT_BASELINE, BASELINE[b]) }
169
+ get letterSpacing(): number { return this._letterSpacing }
170
+ set letterSpacing(px: number) { this._letterSpacing = px; this.push(OP.LETTER_SPACING, px) }
171
+
172
+ // ---- paths ----------------------------------------------------------------------------------
173
+
174
+ beginPath(): this { return this.push(OP.PATH_BEGIN) }
175
+ closePath(): this { return this.push(OP.PATH_CLOSE) }
176
+ moveTo(x: number, y: number): this { return this.push(OP.MOVE_TO, x, y) }
177
+ lineTo(x: number, y: number): this { return this.push(OP.LINE_TO, x, y) }
178
+ quadraticCurveTo(cx: number, cy: number, x: number, y: number): this { return this.push(OP.QUADRATIC_TO, cx, cy, x, y) }
179
+ bezierCurveTo(c1x: number, c1y: number, c2x: number, c2y: number, x: number, y: number): this {
180
+ return this.push(OP.BEZIER_TO, c1x, c1y, c2x, c2y, x, y)
181
+ }
182
+ arc(x: number, y: number, r: number, a0: number, a1: number, ccw = false): this { return this.push(OP.ARC, x, y, r, a0, a1, ccw ? 1 : 0) }
183
+ arcTo(x1: number, y1: number, x2: number, y2: number, r: number): this { return this.push(OP.ARC_TO, x1, y1, x2, y2, r) }
184
+ ellipse(x: number, y: number, rx: number, ry: number, rotation: number, a0: number, a1: number, ccw = false): this {
185
+ return this.push(OP.ELLIPSE, x, y, rx, ry, rotation, a0, a1, ccw ? 1 : 0)
186
+ }
187
+ rect(x: number, y: number, w: number, h: number): this { return this.push(OP.RECT, x, y, w, h) }
188
+ roundRect(x: number, y: number, w: number, h: number, r: number): this { return this.push(OP.ROUND_RECT, x, y, w, h, r) }
189
+
190
+ fill(rule: FillRuleName = "nonzero"): this { return this.push(OP.FILL, RULE[rule]) }
191
+ stroke(): this { return this.push(OP.STROKE) }
192
+ clip(rule: FillRuleName = "nonzero"): this { return this.push(OP.CLIP, RULE[rule]) }
193
+
194
+ // ---- rectangles, text, images ---------------------------------------------------------------
195
+
196
+ fillRect(x: number, y: number, w: number, h: number): this { return this.push(OP.FILL_RECT, x, y, w, h) }
197
+ strokeRect(x: number, y: number, w: number, h: number): this { return this.push(OP.STROKE_RECT, x, y, w, h) }
198
+ clearRect(x: number, y: number, w: number, h: number): this { return this.push(OP.CLEAR_RECT, x, y, w, h) }
199
+ fillText(text: string, x: number, y: number, maxWidth = 0): this { return this.push(OP.FILL_TEXT, this.ref(text), x, y, maxWidth) }
200
+ strokeText(text: string, x: number, y: number, maxWidth = 0): this { return this.push(OP.STROKE_TEXT, this.ref(text), x, y, maxWidth) }
201
+
202
+ /** Blit a surface. Three browser-style forms; the source rect is in the surface's device px. */
203
+ drawImage(img: ImageRef, dx: number, dy: number): this
204
+ drawImage(img: ImageRef, dx: number, dy: number, dw: number, dh: number): this
205
+ drawImage(img: ImageRef, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): this
206
+ drawImage(img: ImageRef, a: number, b: number, c?: number, d?: number, e?: number, f?: number, g?: number, h?: number): this {
207
+ if (e === undefined) {
208
+ return this.push(OP.DRAW_IMAGE, img.surface, 0, 0, img.width, img.height, a, b, c ?? img.width, d ?? img.height)
209
+ }
210
+ return this.push(OP.DRAW_IMAGE, img.surface, a, b, c!, d!, e, f!, g!, h!)
211
+ }
212
+ }
@@ -0,0 +1,201 @@
1
+ // GENERATED by scripts/vendor-canvas.ts from engines/canvas/spec/gen/spec.ts @ ae6bc30b952b — do not edit here.
2
+ // Bump the submodule and run `bun run vendor:canvas`; `bun run check:canvas-ops` fails when this copy is stale.
3
+ // GENERATED by spec/generate.ts from spec/*.h — do not edit.
4
+
5
+ export const SPEC_VERSION = 1
6
+
7
+ export const LineJoin = {
8
+ MITER: 0,
9
+ ROUND: 1,
10
+ BEVEL: 2,
11
+ } as const
12
+ export type LineJoin = (typeof LineJoin)[keyof typeof LineJoin]
13
+ export const LineJoinName: readonly string[] = ["miter", "round", "bevel"]
14
+
15
+ export const LineCap = {
16
+ BUTT: 0,
17
+ ROUND: 1,
18
+ SQUARE: 2,
19
+ } as const
20
+ export type LineCap = (typeof LineCap)[keyof typeof LineCap]
21
+ export const LineCapName: readonly string[] = ["butt", "round", "square"]
22
+
23
+ export const TextAlign = {
24
+ LEFT: 0,
25
+ CENTER: 1,
26
+ RIGHT: 2,
27
+ START: 3,
28
+ END: 4,
29
+ } as const
30
+ export type TextAlign = (typeof TextAlign)[keyof typeof TextAlign]
31
+ export const TextAlignName: readonly string[] = ["left", "center", "right", "start", "end"]
32
+
33
+ export const TextBaseline = {
34
+ ALPHABETIC: 0,
35
+ TOP: 1,
36
+ MIDDLE: 2,
37
+ BOTTOM: 3,
38
+ HANGING: 4,
39
+ IDEOGRAPHIC: 5,
40
+ } as const
41
+ export type TextBaseline = (typeof TextBaseline)[keyof typeof TextBaseline]
42
+ export const TextBaselineName: readonly string[] = ["alphabetic", "top", "middle", "bottom", "hanging", "ideographic"]
43
+
44
+ export const FillRule = {
45
+ NONZERO: 0,
46
+ EVENODD: 1,
47
+ } as const
48
+ export type FillRule = (typeof FillRule)[keyof typeof FillRule]
49
+ export const FillRuleName: readonly string[] = ["nonzero", "evenodd"]
50
+
51
+ export const Spread = {
52
+ PAD: 0,
53
+ REFLECT: 1,
54
+ REPEAT: 2,
55
+ } as const
56
+ export type Spread = (typeof Spread)[keyof typeof Spread]
57
+ export const SpreadName: readonly string[] = ["pad", "reflect", "repeat"]
58
+
59
+ export const Gradient = {
60
+ LINEAR: 0,
61
+ RADIAL: 1,
62
+ } as const
63
+ export type Gradient = (typeof Gradient)[keyof typeof Gradient]
64
+ export const GradientName: readonly string[] = ["linear", "radial"]
65
+
66
+ export const Paint = {
67
+ COLOR: 0,
68
+ LINEAR: 1,
69
+ RADIAL: 2,
70
+ } as const
71
+ export type Paint = (typeof Paint)[keyof typeof Paint]
72
+ export const PaintName: readonly string[] = ["color", "linear", "radial"]
73
+
74
+ export const PathVerb = {
75
+ MOVE: 0,
76
+ LINE: 1,
77
+ QUAD: 2,
78
+ CUBIC: 3,
79
+ CLOSE: 4,
80
+ } as const
81
+ export type PathVerb = (typeof PathVerb)[keyof typeof PathVerb]
82
+ export const PathVerbName: readonly string[] = ["move", "line", "quad", "cubic", "close"]
83
+
84
+ export const ImageFormat = {
85
+ PNG: 0,
86
+ JPEG: 1,
87
+ } as const
88
+ export type ImageFormat = (typeof ImageFormat)[keyof typeof ImageFormat]
89
+ export const ImageFormatName: readonly string[] = ["png", "jpeg"]
90
+
91
+ /** The opcode stream: op id → the word after it. */
92
+ export const OP = {
93
+ SAVE: 0,
94
+ RESTORE: 1,
95
+ TRANSLATE: 2,
96
+ SCALE: 3,
97
+ ROTATE: 4,
98
+ TRANSFORM: 5,
99
+ SET_TRANSFORM: 6,
100
+ RESET_TRANSFORM: 7,
101
+ GLOBAL_ALPHA: 8,
102
+ FILL_STYLE: 9,
103
+ STROKE_STYLE: 10,
104
+ FILL_GRADIENT: 11,
105
+ STROKE_GRADIENT: 12,
106
+ LINE_WIDTH: 13,
107
+ LINE_JOIN: 14,
108
+ LINE_CAP: 15,
109
+ MITER_LIMIT: 16,
110
+ LINE_DASH: 17,
111
+ LINE_DASH_OFFSET: 18,
112
+ FONT: 19,
113
+ TEXT_ALIGN: 20,
114
+ TEXT_BASELINE: 21,
115
+ LETTER_SPACING: 22,
116
+ PATH_BEGIN: 23,
117
+ PATH_CLOSE: 24,
118
+ MOVE_TO: 25,
119
+ LINE_TO: 26,
120
+ QUADRATIC_TO: 27,
121
+ BEZIER_TO: 28,
122
+ ARC: 29,
123
+ ARC_TO: 30,
124
+ ELLIPSE: 31,
125
+ RECT: 32,
126
+ ROUND_RECT: 33,
127
+ FILL: 34,
128
+ STROKE: 35,
129
+ CLIP: 36,
130
+ FILL_RECT: 37,
131
+ STROKE_RECT: 38,
132
+ CLEAR_RECT: 39,
133
+ FILL_TEXT: 40,
134
+ STROKE_TEXT: 41,
135
+ DRAW_IMAGE: 42,
136
+ } as const
137
+ export type OP = (typeof OP)[keyof typeof OP]
138
+ /** Operand layout per op: `args` is one letter per fixed word (f float, i int, r string ref);
139
+ * `repeat` is the group that follows the trailing count. */
140
+ export const OP_ARGS: Record<number, { args: string, repeat: string }> = {
141
+ 0: { args: "", repeat: "" },
142
+ 1: { args: "", repeat: "" },
143
+ 2: { args: "ff", repeat: "" },
144
+ 3: { args: "ff", repeat: "" },
145
+ 4: { args: "f", repeat: "" },
146
+ 5: { args: "ffffff", repeat: "" },
147
+ 6: { args: "ffffff", repeat: "" },
148
+ 7: { args: "", repeat: "" },
149
+ 8: { args: "f", repeat: "" },
150
+ 9: { args: "r", repeat: "" },
151
+ 10: { args: "r", repeat: "" },
152
+ 11: { args: "iffffffi", repeat: "fr" },
153
+ 12: { args: "iffffffi", repeat: "fr" },
154
+ 13: { args: "f", repeat: "" },
155
+ 14: { args: "i", repeat: "" },
156
+ 15: { args: "i", repeat: "" },
157
+ 16: { args: "f", repeat: "" },
158
+ 17: { args: "i", repeat: "f" },
159
+ 18: { args: "f", repeat: "" },
160
+ 19: { args: "r", repeat: "" },
161
+ 20: { args: "i", repeat: "" },
162
+ 21: { args: "i", repeat: "" },
163
+ 22: { args: "f", repeat: "" },
164
+ 23: { args: "", repeat: "" },
165
+ 24: { args: "", repeat: "" },
166
+ 25: { args: "ff", repeat: "" },
167
+ 26: { args: "ff", repeat: "" },
168
+ 27: { args: "ffff", repeat: "" },
169
+ 28: { args: "ffffff", repeat: "" },
170
+ 29: { args: "fffffi", repeat: "" },
171
+ 30: { args: "fffff", repeat: "" },
172
+ 31: { args: "fffffffi", repeat: "" },
173
+ 32: { args: "ffff", repeat: "" },
174
+ 33: { args: "fffff", repeat: "" },
175
+ 34: { args: "i", repeat: "" },
176
+ 35: { args: "", repeat: "" },
177
+ 36: { args: "i", repeat: "" },
178
+ 37: { args: "ffff", repeat: "" },
179
+ 38: { args: "ffff", repeat: "" },
180
+ 39: { args: "ffff", repeat: "" },
181
+ 40: { args: "rfff", repeat: "" },
182
+ 41: { args: "rfff", repeat: "" },
183
+ 42: { args: "iffffffff", repeat: "" },
184
+ }
185
+ export const OP_NAME: readonly string[] = ["save", "restore", "translate", "scale", "rotate", "transform", "setTransform", "resetTransform", "globalAlpha", "fillStyle", "strokeStyle", "fillGradient", "strokeGradient", "lineWidth", "lineJoin", "lineCap", "miterLimit", "lineDash", "lineDashOffset", "font", "textAlign", "textBaseline", "letterSpacing", "pathBegin", "pathClose", "moveTo", "lineTo", "quadraticTo", "bezierTo", "arc", "arcTo", "ellipse", "rect", "roundRect", "fill", "stroke", "clip", "fillRect", "strokeRect", "clearRect", "fillText", "strokeText", "drawImage"]
186
+
187
+ /** The draw list: command id → the word after it. */
188
+ export const DRAW = {
189
+ SET_TRANSFORM: 0,
190
+ SAVE: 1,
191
+ RESTORE: 2,
192
+ CLIP: 3,
193
+ FILL_PATH: 4,
194
+ STROKE_PATH: 5,
195
+ FILL_TEXT: 6,
196
+ STROKE_TEXT: 7,
197
+ DRAW_IMAGE: 8,
198
+ CLEAR_RECT: 9,
199
+ } as const
200
+ export type DRAW = (typeof DRAW)[keyof typeof DRAW]
201
+ export const DRAW_NAME: readonly string[] = ["setTransform", "save", "restore", "clip", "fillPath", "strokePath", "fillText", "strokeText", "drawImage", "clearRect"]
package/src/chisel.ts ADDED
@@ -0,0 +1,193 @@
1
+ /**
2
+ * The chisel vocabulary: every SDK name the bundler's passes key on, declared by the SDK itself.
3
+ *
4
+ * chisel (@letary/chisel) holds the ALGORITHMS — method-granular DCE, chain fusion, curve folding,
5
+ * the UI rewrites — and reads the NAMES from here: compile/bundler.ts passes this object as the
6
+ * `vocab` field of every bundle input, and chisel uses only it. A rename in the SDK is an edit
7
+ * here (tests/compile/chisel-vocab.test.ts fails until it is made), never a chisel release; a chisel
8
+ * release is for algorithms only, or when the schema grows.
9
+ *
10
+ * Rules:
11
+ * - Plain data. No imports, no computed values — chisel's own tests read a JSON copy
12
+ * (`<chisel>/fixtures/vocab.json`, held equal by `bun scripts/chisel-parity.ts --fixture`).
13
+ * - `schema` is the contract version. chisel rejects a newer schema than it implements (upgrade
14
+ * @letary/chisel) and falls back to its built-in defaults for a group an older one lacks.
15
+ * - Role maps (`methods`, `stops`, `range`): the key is the role chisel lowers (its algorithm
16
+ * mirrors that method's body), the value is the SDK's name for it.
17
+ * - Global names (factories, classes, helpers) are the names user code sees — inject.ts exports
18
+ * (`asset` is a host global). The date helpers are module-private names of runtime/datetime.ts:
19
+ * chisel finds them beside `date`.
20
+ * - Changing a list changes bundles (adding a chain method makes more calls provably nodes, so
21
+ * more of them skip the runtime dispatch). Keep the lists exact.
22
+ */
23
+
24
+ export interface ChiselVocab {
25
+ schema: number
26
+ ui: {
27
+ /** Factories going through the variadic `buildUI` dispatch (+ UIPager, which flattens its
28
+ * arguments the same way): literal array arguments are spliced into variadic ones. */
29
+ factories: string[]
30
+ /** factory → its dispatch-free builder (an inject export): a call whose arguments are all
31
+ * provably nodes skips the dispatch. Every key is also in `factories`. */
32
+ raw: Record<string, string>
33
+ /** The other free calls that return a node (argument provability). */
34
+ leaves: string[]
35
+ /** Containers whose function-valued children argument is a reactive children binding. */
36
+ containers: string[]
37
+ /** The text factory: its text argument is a binding position. */
38
+ text: string
39
+ /** Builder methods that keep a chain a node (a conservative whitelist). */
40
+ chain: string[]
41
+ /** The children setter (`x.setContent(() => …)`). */
42
+ setContent: string
43
+ /** Member calls whose object-literal values are bindings (`.style({…})`, `.class({…})`). */
44
+ bindingMethods: string[]
45
+ /** The memoized children-map helper (`__uiMap(list, fn, slot)`). */
46
+ mapHelper: string
47
+ }
48
+ signals: {
49
+ /** Calls that create a signal (the roots of chisel's signal-type inference). */
50
+ factories: string[]
51
+ /** The property a signal is read through. */
52
+ value: string
53
+ }
54
+ fusion: {
55
+ vec3: {
56
+ class: string
57
+ /** The component fields, in order. */
58
+ fields: [ string, string, string ]
59
+ /** Statics that yield a Vec3. */
60
+ statics: string[]
61
+ /** Instance methods that return a Vec3 (local type inference). */
62
+ returning: string[]
63
+ methods: Record<"add" | "sub" | "mul" | "scale" | "negate" | "scaleAndAdd" | "cross" | "normalize" | "dot" | "lengthSq" | "length" | "distanceTo", string>
64
+ }
65
+ date: {
66
+ /** The factory global. */
67
+ factory: string
68
+ /** The value class, and the module-private helpers a fused chain calls (runtime/datetime.ts). */
69
+ class: string
70
+ toMs: string
71
+ formatImpl: string
72
+ timeAgoImpl: string
73
+ /** A value's epoch-ms field. */
74
+ field: string
75
+ /** Methods that return a date (local type inference). */
76
+ returning: string[]
77
+ methods: Record<"add" | "subtract" | "format" | "timeAgo" | "valueOf" | "unix" | "diff" | "isBefore" | "isAfter" | "isSame", string>
78
+ }
79
+ curve: {
80
+ factory: string
81
+ colorFactory: string
82
+ /** The builders' payload getter (a fused chain becomes `{ <data>: new Float32Array([…]) }`). */
83
+ data: string
84
+ /** The bounds of a `Range` object. */
85
+ range: Record<"min" | "max", string>
86
+ /** The stop methods. */
87
+ stops: Record<"from" | "via" | "to" | "fade", string>
88
+ }
89
+ }
90
+ compWrite: {
91
+ /** `__compWrite(owner, prop, axis, v)` / `__compOp(owner, prop, axis, op, v)` — inject exports. */
92
+ write: string
93
+ op: string
94
+ /** The owner's hook the helpers call, and its static list of routed props (stripped once read). */
95
+ hook: string
96
+ list: string
97
+ /** The component names a routed write may target. */
98
+ axes: string[]
99
+ }
100
+ assets: {
101
+ /** `asset("./x")` → the asset's URL, at compile time. */
102
+ macro: string
103
+ }
104
+ }
105
+
106
+ export const chiselVocab: ChiselVocab = {
107
+ schema: 1,
108
+ ui: {
109
+ factories: [
110
+ "UIColumn", "UIRow", "UIBox", "UIButton", "UIScrollable", "UIScreen", "UIWidget",
111
+ "UIModal", "UIPopover", "UIBottomSheet", "UIPager",
112
+ ],
113
+ raw: {
114
+ UIColumn: "__UIColumn", UIRow: "__UIRow", UIBox: "__UIBox", UIButton: "__UIButton",
115
+ UIScrollable: "__UIScrollable", UIScreen: "__UIScreen", UIWidget: "__UIWidget",
116
+ },
117
+ leaves: [
118
+ "UIText", "UIImage", "UIVideo", "UIInput", "UITextArea", "UISpacer",
119
+ "UITabs", "UIVirtualizedList", "NativeView", "CameraView", "MapView", "QRScanner",
120
+ ],
121
+ containers: [ "UIColumn", "UIRow", "UIBox", "UIButton", "UIScrollable", "UIScreen", "UIWidget" ],
122
+ text: "UIText",
123
+ // Exactly the builder methods that return the node (tests/compile/chisel-vocab.test.ts proves it against
124
+ // the interfaces): `animateTo` / `animateFrom` return an Animation, so they are NOT here.
125
+ chain: [
126
+ "style", "class", "named",
127
+ "onClick", "onTouchStart", "onLongPress", "onMouseEnter", "onLayout", "onOpen", "onClose", "onBack",
128
+ "onScroll", "onScrollRelease", "onOverscroll", "onRefresh", "onSubmit",
129
+ "onSelect", "onChange", "onFocus", "onBlur", "onOverlayTap", "onDetent",
130
+ "onEndReached", "onStartReached",
131
+ "append", "insert", "remove", "setContent", "keepAlive",
132
+ "attachTo", "detents", "dismissible", "transition", "setSourceRect",
133
+ "push", "pop", "popToRoot", "replace", "select",
134
+ // UITabs / UIVirtualizedList / the native views (NativeView, CameraView, MapView, QRScanner)
135
+ "badge", "setData", "update", "prepend", "removeByKey",
136
+ "on", "off", "onReady", "onError", "onTap", "onMove", "onScan",
137
+ "flyTo", "jumpTo", "fitPoints", "setPadding", "setUserLocation",
138
+ ],
139
+ setContent: "setContent",
140
+ bindingMethods: [ "style", "class" ],
141
+ mapHelper: "__uiMap",
142
+ },
143
+ signals: {
144
+ factories: [ "signal", "computed" ],
145
+ value: "value",
146
+ },
147
+ fusion: {
148
+ vec3: {
149
+ class: "Vec3",
150
+ fields: [ "x", "y", "z" ],
151
+ statics: [ "up", "down", "left", "right", "forward", "back", "zero", "one", "from" ],
152
+ returning: [
153
+ "add", "sub", "mul", "div", "scale", "negate", "scaleAndAdd", "cross", "lerp", "clamp", "min",
154
+ "max", "reflect", "project", "rotateX", "rotateY", "rotateZ", "rotate", "transform", "withX",
155
+ "withY", "withZ", "clone", "normalize", "copy", "set",
156
+ ],
157
+ methods: {
158
+ add: "add", sub: "sub", mul: "mul", scale: "scale", negate: "negate", scaleAndAdd: "scaleAndAdd",
159
+ cross: "cross", normalize: "normalize", dot: "dot", lengthSq: "lengthSq", length: "length", distanceTo: "distanceTo",
160
+ },
161
+ },
162
+ date: {
163
+ factory: "date",
164
+ class: "DateValue",
165
+ toMs: "toMs",
166
+ formatImpl: "formatImpl",
167
+ timeAgoImpl: "timeAgoImpl",
168
+ field: "t",
169
+ returning: [ "add", "subtract", "startOf", "endOf" ],
170
+ methods: {
171
+ add: "add", subtract: "subtract", format: "format", timeAgo: "timeAgo", valueOf: "valueOf",
172
+ unix: "unix", diff: "diff", isBefore: "isBefore", isAfter: "isAfter", isSame: "isSame",
173
+ },
174
+ },
175
+ curve: {
176
+ factory: "curve",
177
+ colorFactory: "colorCurve",
178
+ data: "_data",
179
+ range: { min: "min", max: "max" },
180
+ stops: { from: "from", via: "via", to: "to", fade: "fade" },
181
+ },
182
+ },
183
+ compWrite: {
184
+ write: "__compWrite",
185
+ op: "__compOp",
186
+ hook: "_writeComp",
187
+ list: "_comps",
188
+ axes: [ "x", "y", "z", "w" ],
189
+ },
190
+ assets: {
191
+ macro: "asset",
192
+ },
193
+ }
@@ -1,4 +1,4 @@
1
- // Ported from packages/lecodes-cli/src/compile/assetMacro.ts (itself from the backend). The
1
+ // Ported from tools/cli/src/compile/assetMacro.ts (itself from the backend). The
2
2
  // compile-time `asset('./x.png')` macro is desugared into a real import so it goes through the same
3
3
  // loader/resolver as a hand-written import.
4
4
 
@@ -7,6 +7,7 @@ import { collectAssetMacros, desugarAssetMacros, hasAssetMacro, type AssetMacroR
7
7
  import { scanModulesFallback, type ScanModule } from "./detectEntry"
8
8
  import { hasAspectExtends, injectAspectNames } from "./aspectMacro"
9
9
  import { hasImportStatement, rewriteLibraryImports } from "./libraryImports"
10
+ import { chiselVocab } from "../chisel"
10
11
 
11
12
  /*
12
13
  * The user-project bundler. Project files live in an in-memory map; the whole SDK is delivered via
@@ -49,7 +50,9 @@ const normalize = (path: string): string => {
49
50
  // `EDITOR` is a boolean define: `false` (const-folded, `if (EDITOR)` branches DCE'd away) in
50
51
  // every shipped bundle, `true` only in scene-editor compiles (CompileOptions.editor). Requires a
51
52
  // chisel with boolean-define support — an older binary ignores the entry and leaves `EDITOR` as a
52
- // free identifier (a runtime ReferenceError only in code that actually uses it).
53
+ // free identifier (a runtime ReferenceError in code that uses it). The SDK itself uses it: the
54
+ // edit-mode branches of src/scene/defineScene.ts read `EDITOR && isEditMode()`, so a shipped bundle
55
+ // carries no editor code (sdk/tests/scene/editor-contract.test.ts).
53
56
  const CHISEL_DEFINE: Record<string, string> = { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" }
54
57
 
55
58
  // Methods the native host invokes by name have no in-bundle caller, so method-DCE can't see them.
@@ -62,6 +65,9 @@ const CHISEL_KEEP = [ "_*" ]
62
65
  * src/server/inject.ts). A bundle gets exactly one of them. */
63
66
  export const CLIENT_INJECT = "/__sdk/inject.ts"
64
67
  export const SERVER_INJECT = "/__sdk/server/inject.ts"
68
+ /** What an EDITOR bundle gets on top of the client SDK (`editor: true`): the scene editor's
69
+ * contract, `__sceneEditor` (src/inject.editor.ts → src/scene/editor.ts). Never in a shipped bundle. */
70
+ export const EDITOR_INJECT = "/__sdk/inject.editor.ts"
65
71
 
66
72
  /** The SDK source root (parent of `inject.ts`). chisel needs the source tree, not a prebuilt bundle. */
67
73
  const resolveSdkSrc = (): string => {
@@ -248,7 +254,7 @@ const bundleProjectChisel = async (
248
254
  const input = JSON.stringify({
249
255
  files: { ...sdkFiles, ...projFiles },
250
256
  entry,
251
- inject: [ inject ],
257
+ inject: editor ? [ inject, EDITOR_INJECT ] : [ inject ],
252
258
  format,
253
259
  minify,
254
260
  // Chain fusion, ON by default (`LECODES_CHISEL_FUSE=0` opts out): Vec3/date math chains lower to
@@ -268,6 +274,9 @@ const bundleProjectChisel = async (
268
274
  // this only drops an array allocation per call site. Safe to always request: the sibling SDK
269
275
  // source above has the variadic dispatch, and an older binary ignores the unknown field.
270
276
  flatten_ui: process.env.LECODES_CHISEL_FLATTEN_UI !== "0",
277
+ // The SDK names the passes key on (src/chisel.ts). chisel uses only these; a chisel that
278
+ // predates the field ignores it and keeps its built-in lists.
279
+ vocab: chiselVocab,
271
280
  })
272
281
 
273
282
  const bin = resolveChiselBin()