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
@@ -1,12 +1,13 @@
1
- import { ClickEvent, TouchStartEvent, LongPressEvent } from "../runtime/touch"
2
- import type { ClickEvent as ClickEventType, TouchStartEvent as TouchStartEventType, LongPressEvent as LongPressEventType } from "../runtime/touch"
3
- import { type ElementStyle, type ContainerStyle, ContainerElement, buildUI, type DrawableStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type BaseStyle, type Color, type LayerEasing } from "./UINode"
1
+ import { ClickEvent, TouchStartEvent, LongPressEvent, MouseEnterEvent } from "../runtime/touch"
2
+ import type { ClickEvent as ClickEventType, TouchStartEvent as TouchStartEventType, LongPressEvent as LongPressEventType, MouseEnterEvent as MouseEnterEventType } from "../runtime/touch"
3
+ import { type ElementStyle, type ContainerStyle, ContainerElement, buildUI, type DrawableStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type BaseStyle, type Color } from "./UINode"
4
+ import { TREE_FLAG_CLICK, TREE_FLAG_HOVER, TREE_FLAG_INTERACTIVE, TREE_FLAG_LONG_PRESS, tree } from "./tree"
4
5
 
5
6
  export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & { rippleColor?: Color | "default" }
6
7
 
7
- /** A tappable container — row + centered children by default. Press feedback via the `onPressed`
8
- * style block, the `$pressed` class, or `rippleColor`. */
9
- export interface UIButton extends UIContainerBase<UIButtonStyle & { onPressed?: UIButtonStyle & { duration?: number, delay?: number, easing?: LayerEasing } }, DrawableStyle & BaseStyle> {
8
+ /** A tappable container — row + centered children by default. Press feedback via the `$pressed`
9
+ * class block or `rippleColor`. */
10
+ export interface UIButton extends UIContainerBase<UIButtonStyle, DrawableStyle & BaseStyle> {
10
11
  readonly type: "button",
11
12
 
12
13
  /** Fired on a completed tap/click. */
@@ -17,8 +18,14 @@ export interface UIButton extends UIContainerBase<UIButtonStyle & { onPressed?:
17
18
  * `ev.track(...)` the rest of the gesture (like `onTouchStart`), so a hold can flow straight into
18
19
  * dragging the element. A handled long-press suppresses the click that would follow the release. */
19
20
  onLongPress(callback: (ev: LongPressEventType<UIButton>) => void): this,
21
+ /** Fired when a mouse cursor (no button) enters the button — desktop / web; touch never fires it.
22
+ * There is no leave event: `ev.track({ onMove, onEnd, onCancel })` follows the hover to its
23
+ * end. The visual state is the reserved `$hovered` class (cascades like `$pressed`). */
24
+ onMouseEnter(callback: (ev: MouseEnterEventType<UIButton>) => void): this,
20
25
  /** Is a finger currently down on the button? */
21
- isPressed(): boolean,
26
+ readonly isPressed: boolean,
27
+ /** Is a mouse cursor currently over the button (the topmost interactive node under it)? */
28
+ readonly isHovered: boolean,
22
29
  }
23
30
 
24
31
 
@@ -27,11 +34,19 @@ class ButtonElement extends ContainerElement<"button"> {
27
34
  readonly clickListeners: any[] = []
28
35
  readonly touchStartListeners: any[] = []
29
36
  readonly longPressListeners: any[] = []
37
+ readonly mouseEnterListeners: any[] = []
38
+
39
+ constructor(type: "button", style: any, children: any) {
40
+ super(type, style, children)
41
+ this._addFlags(TREE_FLAG_INTERACTIVE) // a button is a touch target from the start (press feedback)
42
+ }
30
43
 
31
44
  onClick (callback: any): this {
32
45
  this.clickListeners.push(callback)
46
+ this._addFlags(TREE_FLAG_CLICK)
33
47
  return this
34
48
  }
49
+ /** @internal */
35
50
  _emitClick(pointerId: number, clientX: number, clientY: number) {
36
51
  const touchEvent = new ClickEvent(clientX, clientY, pointerId)
37
52
  touchEvent.target = this
@@ -45,6 +60,7 @@ class ButtonElement extends ContainerElement<"button"> {
45
60
  this.touchStartListeners.push(callback)
46
61
  return this
47
62
  }
63
+ /** @internal */
48
64
  _emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
49
65
  const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
50
66
  touchEvent.target = this
@@ -59,12 +75,13 @@ class ButtonElement extends ContainerElement<"button"> {
59
75
 
60
76
  onLongPress (callback: any): this {
61
77
  this.longPressListeners.push(callback)
78
+ this._addFlags(TREE_FLAG_LONG_PRESS)
62
79
  return this
63
80
  }
64
- // Fired by the host when the finger is held past the long-press threshold. Returns null when
65
- // nothing is listening — the host then lets the trailing click through as usual. Otherwise it
66
- // returns an object whose presence tells the host to swallow that click, carrying the optional
67
- // drag handler a listener registered via `ev.track(...)` for the host to drive the gesture.
81
+ /** @internal Fired by the host when the finger is held past the long-press threshold. Returns
82
+ * null when nothing is listening — the trailing click then goes through as usual. Otherwise the
83
+ * wrapper whose presence swallows that click, carrying the optional drag handler a listener
84
+ * registered via `ev.track(...)`. */
68
85
  _emitLongPress(pointerId: number, clientX: number, clientY: number): any {
69
86
  if (this.longPressListeners.length === 0) return null
70
87
  const longPressEvent = new LongPressEvent(clientX, clientY, pointerId)
@@ -75,9 +92,32 @@ class ButtonElement extends ContainerElement<"button"> {
75
92
  return { track: (longPressEvent as any)._trackHandler ?? null }
76
93
  }
77
94
 
78
- isPressed(): boolean {
79
- if (!this._id) return false
80
- return _creatorUI.isButtonPressed(this._id)
95
+ get isPressed(): boolean {
96
+ if (!this._h.id) return false
97
+ return tree().isButtonPressed(this._h.id)
98
+ }
99
+
100
+ onMouseEnter (callback: any): this {
101
+ this.mouseEnterListeners.push(callback)
102
+ this._addFlags(TREE_FLAG_HOVER) // the runtime crosses into JS for hover only on flagged nodes
103
+ return this
104
+ }
105
+ /** @internal Fired by the runtime when the cursor enters (TREE_EVENT_HOVER_ENTER). Returns the
106
+ * hover track handler a listener registered via `ev.track(...)`, or null — the emitter keeps it
107
+ * in its one hover slot and answers whether moves are wanted. */
108
+ _emitMouseEnter(clientX: number, clientY: number): any {
109
+ const ev = new MouseEnterEvent(clientX, clientY)
110
+ ev.target = this
111
+ for (let callback of this.mouseEnterListeners) {
112
+ callback(ev)
113
+ if ((ev as any)._trackHandler) return (ev as any)._trackHandler
114
+ }
115
+ return null
116
+ }
117
+
118
+ get isHovered(): boolean {
119
+ if (!this._h.id) return false
120
+ return tree().isHovered(this._h.id)
81
121
  }
82
122
  }
83
123
 
@@ -94,8 +134,6 @@ export const __UIButton = (children: UINodeChild[] | ChildrenFn): UIButton => ma
94
134
  * `UIColumn`; attach behavior with `.onClick(...)`. */
95
135
  export function UIButton(...children: UIChildArg[]): UIButton;
96
136
  export function UIButton(children: UINodeChild[] | ChildrenFn): UIButton;
97
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
98
- export function UIButton(style: UIButtonStyle, children?: UINodeChild[] | ChildrenFn): UIButton;
99
137
  export function UIButton(...args: any[]): UIButton {
100
138
  return buildUI(args, BUTTON_DEFAULTS, makeButton)
101
139
  }
@@ -19,8 +19,6 @@ export const __UIBox = (children: UINodeChild[] | ChildrenFn): UIBox => makeBox(
19
19
  * entries are skipped; a lone function makes the children reactive. */
20
20
  export function UIColumn(...children: UIChildArg[]): UIColumn
21
21
  export function UIColumn(children: UINodeChild[] | ChildrenFn): UIColumn
22
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
23
- export function UIColumn(style: UIContainerStyle, children?: UINodeChild[] | ChildrenFn): UIColumn
24
22
  export function UIColumn(...args: any[]): UIColumn {
25
23
  return buildUI(args, null, makeColumn)
26
24
  }
@@ -35,8 +33,6 @@ const makeRow = (style: any, children: any) => new ContainerElement("row", style
35
33
  /** Create a horizontal flex container (same argument forms as `UIColumn`). */
36
34
  export function UIRow(...children: UIChildArg[]): UIRow
37
35
  export function UIRow(children: UINodeChild[] | ChildrenFn): UIRow
38
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
39
- export function UIRow(style: UIContainerStyle, children?: UINodeChild[] | ChildrenFn): UIRow
40
36
  export function UIRow(...args: any[]): UIRow {
41
37
  return buildUI(args, ROW_DEFAULTS, makeRow)
42
38
  }
@@ -53,8 +49,6 @@ const makeBox = (style: any, children: any) => new ContainerElement("box", style
53
49
  * avatars, empty states). Same argument forms as `UIColumn`. */
54
50
  export function UIBox(...children: UIChildArg[]): UIBox
55
51
  export function UIBox(children: UINodeChild[] | ChildrenFn): UIBox
56
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
57
- export function UIBox(style: UIContainerStyle, children?: UINodeChild[] | ChildrenFn): UIBox
58
52
  export function UIBox(...args: any[]): UIBox {
59
53
  return buildUI(args, BOX_DEFAULTS, makeBox)
60
54
  }
package/src/ui/UIImage.ts CHANGED
@@ -4,11 +4,12 @@ import type { File } from "../runtime/fetch"
4
4
  import { Canvas, isCanvas } from "../canvas/Canvas"
5
5
  import type { Scene2D } from "../g2/Scene2D"
6
6
  import { Element, type Color, type ElementStyle, type UIElementBase } from "./UINode"
7
+ import { wireColor } from "./styleColor"
8
+ import { tree } from "./tree"
7
9
 
8
10
  export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "cover" | "contain" | "fill", tintColor?: Color }
9
11
 
10
- /** A baked Canvas surface as an image source. The UI backend resolves it to a UI texture — see
11
- * docs/canvas-contract.md ("Pending"). */
12
+ /** A baked Canvas surface as an image source. The UI backend resolves it to a UI texture. */
12
13
  export type CanvasImageSource = { canvasSurface: number }
13
14
 
14
15
  /** A live 2D scene as an image source: the host draws the scene into the node's box every frame
@@ -50,48 +51,51 @@ export interface UIImage extends UIElementBase<UIImageStyle> {
50
51
  }
51
52
 
52
53
  class ImageElement extends Element<"image"> {
53
- private _src: ResolvedImageSource
54
- // Current source sub-rect; undefined until the first setSourceRect. Kept on the node (not
55
- // just sent as a command) so a crop set while detached (_id == 0) is reapplied at mount —
56
- // the viewer reads it when it creates the element.
57
- private _sourceRect?: [number, number, number, number]
54
+ private _src!: ResolvedImageSource
58
55
  constructor(style: any, src: ImageSource) {
59
56
  super("image", style)
60
- this._src = toSrc(src)
57
+ this.src = src
61
58
  }
62
59
 
63
60
  get src(): ResolvedImageSource {
64
61
  return this._src
65
62
  }
66
63
  set src(value: ImageSource) {
67
- this._src = toSrc(value)
68
- if (this._id !== 0) {
69
- _creatorUI.updateNode(this._id, "src", this._src)
64
+ const next = toSrc(value)
65
+ // A source tint that is not a colour throws BEFORE anything is applied, like a style colour.
66
+ const n: any = next
67
+ const tint = n && typeof n === "object" && "svg" in n && n.tintColor != null ? wireColor("tintColor", n.tintColor) as string : null
68
+ this._src = next
69
+ if (this._h.id === 0) return
70
+ // The wire spellings of a source (the host resolves each prefix): a url as is, "id:<bufferId>"
71
+ // for a fetch / asset buffer, "svg:<source>" (+ "srcTintColor", '#rrggbbaa' or a var(), AFTER
72
+ // the src: a src write resets the source tint), "canvas:<surfaceId>", "scene2d:<sceneId>".
73
+ const s: any = this._src
74
+ if (typeof s === "string") {
75
+ tree().setString(this._h.id, "src", s)
76
+ } else if (s && typeof s === "object") {
77
+ if ("svg" in s) {
78
+ tree().setString(this._h.id, "src", "svg:" + s.svg)
79
+ if (tint != null) tree().setString(this._h.id, "srcTintColor", tint)
80
+ } else if ("canvasSurface" in s) {
81
+ tree().setString(this._h.id, "src", "canvas:" + s.canvasSurface)
82
+ } else if ("scene2d" in s) {
83
+ tree().setString(this._h.id, "src", "scene2d:" + s.scene2d)
84
+ } else if ("_h" in s) {
85
+ tree().setString(this._h.id, "src", "id:" + s._h.id)
86
+ }
70
87
  }
71
88
  }
72
89
 
73
90
  setSourceRect(x: number, y: number, w: number, h: number): this {
74
- // Stash on the node so a crop set before mount is reapplied when the element is created.
75
- this._sourceRect = [x, y, w, h]
76
- // Live mutation: fast channel (not _style → no PX_VALUE density scaling: these are texture
77
- // pixels, a different coordinate space). On a detached node there is nothing to mutate yet —
78
- // the stash above carries it to mount. The viewer decides relayout vs draw-only by comparing
79
- // w/h against the rect it has stashed on the element.
80
- if (this._id !== 0) {
81
- _creatorUI.setSourceRect(this._id, x, y, w, h)
82
- }
91
+ // Texture pixels (not a style → no density scaling); the host decides relayout vs draw-only by
92
+ // comparing w/h against the rect it has.
93
+ if (this._h.id !== 0) tree().setString(this._h.id, "sourceRect", `${x} ${y} ${w} ${h}`)
83
94
  return this
84
95
  }
85
96
  }
86
97
 
87
98
  /** Create an image element from a source (see {@link ImageSource}). */
88
- export function UIImage(src: ImageSource): UIImage
89
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
90
- export function UIImage(style: UIImageStyle, src: ImageSource): UIImage
91
- export function UIImage(...args: [ImageSource] | [UIImageStyle, ImageSource]): UIImage {
92
- if (args.length === 1) {
93
- return new ImageElement({}, args[0])
94
- } else {
95
- return new ImageElement(args[0], args[1])
96
- }
97
- }
99
+ export function UIImage(src: ImageSource): UIImage {
100
+ return new ImageElement({}, src)
101
+ }
package/src/ui/UIInput.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type ElementStyle, type DrawableStyle, Element, type TextStyle, type Color, type BaseStyle, type UIElementBase, type LayerEasing } from "./UINode";
1
+ import { type ElementStyle, type DrawableStyle, Element, type TextStyle, type Color, type BaseStyle, type UIElementBase } from "./UINode";
2
+ import { TREE_FLAG_BLUR, TREE_FLAG_CHANGE, TREE_FLAG_FOCUS, TREE_FLAG_SUBMIT, tree } from "./tree"
2
3
 
3
4
  type InputStyle = {
4
5
  placeholder?: string,
@@ -51,15 +52,11 @@ type SubmitStyle = {
51
52
 
52
53
  /** A single-line text input. Keyboard/picker kind via the `type` style, return-key behavior via
53
54
  * `enterKey` + `onSubmit`; keyboard policy on `keyboardShrink`/`keyboardDismiss`. */
54
- export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle & { onFocused?: UIInputStyle & { duration?: number, delay?: number, easing?: LayerEasing } }, DrawableStyle & BaseStyle> {
55
+ export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle, DrawableStyle & BaseStyle> {
55
56
  readonly type: "input",
56
57
 
57
- /**
58
- * The field's text. Writable at any time — set it BEFORE the screen opens to pre-fill a form
59
- * (settings, edit-profile); the value is stashed on the node and applied when the host builds
60
- * it. Reads return the live text while the screen is open, and the last value you set while it
61
- * isn't. The user's typing is not preserved across close/open (use `keepAlive()` for that).
62
- */
58
+ /** The field's text. Writable at any time; reads return the live text (the host owns the field
59
+ * while the user types — IME composition, paste, the maxLength clamp, date/time canonicalization). */
63
60
  value: string
64
61
 
65
62
  /** The text changed — fires on every user edit. */
@@ -71,17 +68,17 @@ export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle & { on
71
68
  /** The keyboard's return key was pressed. `value` is the current text. See `enterKey`. */
72
69
  onSubmit(callback: (value: string) => void): UIInput
73
70
 
74
- /** Focus the input (opens the keyboard). No-op before mount. */
71
+ /** Focus the input (opens the keyboard). */
75
72
  focus(): void
76
73
  /** Release focus (dismisses the keyboard when this input held it). */
77
74
  blur(): void
78
75
  }
79
76
 
80
77
  /** A multi-line text field — see the {@link UITextArea} factory for the auto-grow contract. */
81
- export interface UITextArea extends UIElementBase<UIInputStyle & { onFocused?: UIInputStyle & { duration?: number, delay?: number, easing?: LayerEasing } }, DrawableStyle & BaseStyle> {
78
+ export interface UITextArea extends UIElementBase<UIInputStyle, DrawableStyle & BaseStyle> {
82
79
  readonly type: "textarea",
83
80
 
84
- /** The field's text — same contract as {@link UIInput.value}: settable before mount to pre-fill. */
81
+ /** The field's text — same contract as {@link UIInput.value}. */
85
82
  value: string
86
83
 
87
84
  /** The text changed — fires on every user edit. */
@@ -91,7 +88,7 @@ export interface UITextArea extends UIElementBase<UIInputStyle & { onFocused?: U
91
88
  /** Lost focus. */
92
89
  onBlur(callback: () => void): UITextArea
93
90
 
94
- /** Focus the text area (opens the keyboard). No-op before mount. */
91
+ /** Focus the text area (opens the keyboard). */
95
92
  focus(): void
96
93
  /** Release focus (dismisses the keyboard when this text area held it). */
97
94
  blur(): void
@@ -104,25 +101,12 @@ class InputElement<T extends "input" | "textarea"> extends Element<T> {
104
101
  readonly bl: Function[] = []
105
102
  readonly sl: Function[] = []
106
103
 
107
- /** @internal The value as last set by CODE. Hosts read it at node creation (after the initial
108
- * style, so a `date` field's canonical parse and `maxLength` are already in place), which is
109
- * what makes a value assigned before mount — a pre-filled settings form — land on the field.
110
- * Same pre-mount stash as UIImage's `_sourceRect`. Typing does NOT sync back here: while
111
- * mounted the HOST owns the text (IME composition, paste, the maxLength clamp, date/time
112
- * canonicalization) and the getter reads it over the bridge. So on a remount the field comes
113
- * back with the last code-set value, not what the user typed — screens that must keep live
114
- * editing state across close/open use `keepAlive()`, the mechanism built for it. */
115
- _value = ""
116
-
117
- get value() {
118
- // Mounted: the host is authoritative. Unmounted: what code last set (0 => never mounted).
119
- return this._id !== 0 ? _creatorUI.getTextValue(this._id) : this._value
104
+ get value(): string {
105
+ // The host is authoritative for the field's text (typing never round-trips through JS).
106
+ return this._h.id !== 0 ? tree().getTextValue(this._h.id) : ""
120
107
  }
121
108
  set value(value: string) {
122
- this._value = value
123
- if (this._id !== 0) {
124
- _creatorUI.updateNode(this._id, "value", value)
125
- }
109
+ if (this._h.id !== 0) tree().setString(this._h.id, "value", value)
126
110
  }
127
111
 
128
112
  // `phone` is SDK-surface only — hosts (and the web's verbatim `el.type`) keep HTML's `tel` as
@@ -133,31 +117,39 @@ class InputElement<T extends "input" | "textarea"> extends Element<T> {
133
117
 
134
118
  onChange (callback: (value: string) => void): this {
135
119
  this.cl.push(callback)
120
+ this._addFlags(TREE_FLAG_CHANGE)
136
121
  return this
137
122
  }
138
123
  onFocus(callback: () => void): this {
139
124
  this.fl.push(callback)
125
+ this._addFlags(TREE_FLAG_FOCUS)
140
126
  return this
141
127
  }
142
128
  onBlur(callback: () => void): this {
143
129
  this.bl.push(callback)
130
+ this._addFlags(TREE_FLAG_BLUR)
144
131
  return this
145
132
  }
146
133
  onSubmit(callback: (value: string) => void): this {
147
134
  this.sl.push(callback)
135
+ this._addFlags(TREE_FLAG_SUBMIT)
148
136
  return this
149
137
  }
150
-
151
- // Focus rides the existing value channel (updateNode "focus") — no new ABI method.
138
+ /** @internal */
139
+ _emitChange(value: string): void { for (const cb of this.cl.slice()) cb(value) }
140
+ /** @internal */
141
+ _emitFocus(): void { for (const cb of this.fl.slice()) cb() }
142
+ /** @internal */
143
+ _emitBlur(): void { for (const cb of this.bl.slice()) cb() }
144
+ /** @internal */
145
+ _emitSubmit(value: string): void { for (const cb of this.sl.slice()) cb(value) }
146
+
147
+ // Focus rides the content channel ("focus" = "1" / "0") — no dedicated bridge method.
152
148
  focus(): void {
153
- if (this._id !== 0) {
154
- _creatorUI.updateNode(this._id, "focus", true)
155
- }
149
+ if (this._h.id !== 0) tree().setString(this._h.id, "focus", "1")
156
150
  }
157
151
  blur(): void {
158
- if (this._id !== 0) {
159
- _creatorUI.updateNode(this._id, "focus", false)
160
- }
152
+ if (this._h.id !== 0) tree().setString(this._h.id, "focus", "0")
161
153
  }
162
154
  }
163
155
 
package/src/ui/UIModal.ts CHANGED
@@ -63,7 +63,7 @@ export class ModalElement extends WidgetElement {
63
63
  constructor(style: UIModalStyle, children: UINodeChild[] | ChildrenFn) {
64
64
  super("widget", { overlayColor: "rgba(0, 0, 0, 0.5)", ...style }, children)
65
65
  this.onOverlayTap(() => { if (this._dismissible) this.hide() })
66
- this.onBackPressed(() => { if (this._dismissible) this.hide() })
66
+ this.onBack(() => { if (this._dismissible) this.hide() })
67
67
  }
68
68
 
69
69
  // The full animate argument: hidden pose + duration, plus the scrim fade — only when the
@@ -132,8 +132,6 @@ const makeModal = (style: any, children: any) => new ModalElement(style, childre
132
132
  * `show()`/`hide()`. Same argument forms as `UIColumn`. */
133
133
  export function UIModal(...children: UIChildArg[]): UIModal;
134
134
  export function UIModal(children: UINodeChild[] | ChildrenFn): UIModal;
135
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
136
- export function UIModal(style: UIModalStyle, children?: UINodeChild[] | ChildrenFn): UIModal;
137
135
  export function UIModal(...args: any[]): UIModal {
138
136
  return buildUI(args, null, makeModal)
139
137
  }