lecodes-cli 0.18.2 → 0.19.2

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 (224) hide show
  1. package/dist/index.js +1866 -301
  2. package/package.json +14 -12
  3. package/runtime/scene-harness.json +1 -1
  4. package/runtime/sdk-types.json +1 -1
  5. package/src/api.ts +302 -0
  6. package/src/browserAuth.ts +87 -0
  7. package/src/cmgenTool.ts +104 -0
  8. package/src/commands/app.ts +891 -0
  9. package/src/commands/appAndroid.ts +603 -0
  10. package/src/commands/appDesktop.ts +326 -0
  11. package/src/commands/appDesktopMac.ts +470 -0
  12. package/src/commands/appIcon.ts +187 -0
  13. package/src/commands/appShared.ts +448 -0
  14. package/src/commands/appTemplates.ts +883 -0
  15. package/src/commands/appTemplatesAndroid.ts +599 -0
  16. package/src/commands/appTemplatesGradlew.ts +9 -0
  17. package/src/commands/assets.ts +28 -0
  18. package/src/commands/clone.ts +60 -0
  19. package/src/commands/compile.ts +253 -0
  20. package/src/commands/create.ts +59 -0
  21. package/src/commands/design.ts +863 -0
  22. package/src/commands/designTemplates.ts +11 -0
  23. package/src/commands/desktop.ts +122 -0
  24. package/src/commands/dev.ts +214 -0
  25. package/src/commands/diff.ts +59 -0
  26. package/src/commands/init.ts +191 -0
  27. package/src/commands/install.ts +198 -0
  28. package/src/commands/lightmap.ts +290 -0
  29. package/src/commands/link.ts +147 -0
  30. package/src/commands/login.ts +24 -0
  31. package/src/commands/navmesh.ts +225 -0
  32. package/src/commands/pn.ts +305 -0
  33. package/src/commands/projectTemplates.ts +129 -0
  34. package/src/commands/pull.ts +109 -0
  35. package/src/commands/push.ts +173 -0
  36. package/src/commands/render.ts +414 -0
  37. package/src/commands/scene.ts +148 -0
  38. package/src/commands/shaders.ts +157 -0
  39. package/src/commands/status.ts +29 -0
  40. package/src/commands/test.ts +370 -0
  41. package/src/commands/thumbs.ts +178 -0
  42. package/src/commands/types.ts +87 -0
  43. package/src/commands/update.ts +190 -0
  44. package/src/compile/assetIcons.ts +214 -0
  45. package/src/compile/collect.ts +46 -0
  46. package/src/compile/collectLocal.ts +37 -0
  47. package/src/compile/designCompile.ts +104 -0
  48. package/src/compile/fonts.ts +156 -0
  49. package/src/compile/headlessBundle.ts +129 -0
  50. package/src/compile/nativeStack.ts +21 -0
  51. package/src/compile/projectCompile.ts +109 -0
  52. package/src/compile/sceneCompile.ts +113 -0
  53. package/src/compile/screenEntry.ts +127 -0
  54. package/src/compile/shaders.ts +245 -0
  55. package/src/config.ts +42 -0
  56. package/src/designMeta.ts +35 -0
  57. package/src/desktopRenderer.ts +532 -0
  58. package/src/desktopScript.ts +276 -0
  59. package/src/dev/clientTemplates.ts +160 -0
  60. package/src/dev/devServer.ts +292 -0
  61. package/src/dev/wsServer.ts +144 -0
  62. package/src/distRoot.ts +20 -0
  63. package/src/ignore.ts +163 -0
  64. package/src/index.ts +491 -0
  65. package/src/lecodes-3d-editor.d.ts +41 -0
  66. package/src/lecodes-assets.d.ts +7 -0
  67. package/src/lecodes-design.d.ts +191 -0
  68. package/src/lecodes-renderer.d.ts +131 -0
  69. package/src/localFiles.ts +144 -0
  70. package/src/manifest.ts +46 -0
  71. package/src/matcTool.ts +137 -0
  72. package/src/peers.ts +40 -0
  73. package/src/project.ts +61 -0
  74. package/src/projectEnv.ts +94 -0
  75. package/src/qrcode-terminal.d.ts +9 -0
  76. package/src/releases.ts +125 -0
  77. package/src/serverDiff.ts +78 -0
  78. package/src/textDiff.ts +103 -0
  79. package/src/types.ts +0 -0
  80. package/src/util.ts +146 -0
  81. package/runtime/sdk/animate/animate.ts +0 -238
  82. package/runtime/sdk/animate/bezier.ts +0 -138
  83. package/runtime/sdk/animate/easings.ts +0 -126
  84. package/runtime/sdk/canvas/Canvas.ts +0 -305
  85. package/runtime/sdk/core/Aspect.ts +0 -512
  86. package/runtime/sdk/core/InspectorUI.ts +0 -212
  87. package/runtime/sdk/core/color.ts +0 -66
  88. package/runtime/sdk/core/compWrite.ts +0 -42
  89. package/runtime/sdk/core/events.ts +0 -38
  90. package/runtime/sdk/core/fields.ts +0 -120
  91. package/runtime/sdk/core/registry.ts +0 -23
  92. package/runtime/sdk/core/signals.ts +0 -277
  93. package/runtime/sdk/core/time.ts +0 -81
  94. package/runtime/sdk/g2/Camera2D.ts +0 -40
  95. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  96. package/runtime/sdk/g2/Node2D.ts +0 -267
  97. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  98. package/runtime/sdk/g2/Physics2D.ts +0 -346
  99. package/runtime/sdk/g2/Scene2D.ts +0 -209
  100. package/runtime/sdk/g2/Shape2D.ts +0 -259
  101. package/runtime/sdk/g2/Sprite.ts +0 -89
  102. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  103. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  104. package/runtime/sdk/g2/Texture2D.ts +0 -47
  105. package/runtime/sdk/g2/Tilemap.ts +0 -41
  106. package/runtime/sdk/g2/Tileset.ts +0 -71
  107. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  108. package/runtime/sdk/g2/autotile.ts +0 -433
  109. package/runtime/sdk/g2/cells.ts +0 -91
  110. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  111. package/runtime/sdk/g2/groups2d.ts +0 -106
  112. package/runtime/sdk/g2/loop.ts +0 -50
  113. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  114. package/runtime/sdk/g2/touch.ts +0 -83
  115. package/runtime/sdk/gl/Camera.ts +0 -160
  116. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  117. package/runtime/sdk/gl/CharacterController.ts +0 -238
  118. package/runtime/sdk/gl/Gearbox.ts +0 -212
  119. package/runtime/sdk/gl/Geometry.ts +0 -279
  120. package/runtime/sdk/gl/IK.ts +0 -193
  121. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  122. package/runtime/sdk/gl/Light.ts +0 -99
  123. package/runtime/sdk/gl/Lightmap.ts +0 -179
  124. package/runtime/sdk/gl/Material.ts +0 -245
  125. package/runtime/sdk/gl/Mesh.ts +0 -83
  126. package/runtime/sdk/gl/Model.ts +0 -64
  127. package/runtime/sdk/gl/Node.ts +0 -350
  128. package/runtime/sdk/gl/Noise.ts +0 -30
  129. package/runtime/sdk/gl/Particles.ts +0 -676
  130. package/runtime/sdk/gl/Physics.ts +0 -222
  131. package/runtime/sdk/gl/Plane.ts +0 -53
  132. package/runtime/sdk/gl/Ray.ts +0 -16
  133. package/runtime/sdk/gl/Scene.ts +0 -479
  134. package/runtime/sdk/gl/Shape.ts +0 -377
  135. package/runtime/sdk/gl/Texture.ts +0 -46
  136. package/runtime/sdk/gl/Trigger.ts +0 -45
  137. package/runtime/sdk/gl/Vehicle.ts +0 -473
  138. package/runtime/sdk/gl/Wheel.ts +0 -240
  139. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  140. package/runtime/sdk/gl/animation/Animator.ts +0 -87
  141. package/runtime/sdk/gl/animation/Layer.ts +0 -29
  142. package/runtime/sdk/gl/animation/Loop.ts +0 -25
  143. package/runtime/sdk/gl/animation/Playback.ts +0 -43
  144. package/runtime/sdk/gl/animation/core.ts +0 -294
  145. package/runtime/sdk/gl/controls.ts +0 -95
  146. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  147. package/runtime/sdk/gl/scenarios.ts +0 -291
  148. package/runtime/sdk/gl/state.ts +0 -6
  149. package/runtime/sdk/gl/touch.ts +0 -68
  150. package/runtime/sdk/inject.ts +0 -186
  151. package/runtime/sdk/math/Mathf.ts +0 -118
  152. package/runtime/sdk/math/mat4.ts +0 -278
  153. package/runtime/sdk/math/quat.ts +0 -232
  154. package/runtime/sdk/math/vec.ts +0 -255
  155. package/runtime/sdk/plugins/camera.ts +0 -81
  156. package/runtime/sdk/plugins/geolocation.ts +0 -123
  157. package/runtime/sdk/plugins/oauth.ts +0 -61
  158. package/runtime/sdk/plugins/permission.ts +0 -7
  159. package/runtime/sdk/plugins/push.ts +0 -132
  160. package/runtime/sdk/plugins/qr.ts +0 -73
  161. package/runtime/sdk/plugins/service.ts +0 -47
  162. package/runtime/sdk/runtime/app.ts +0 -101
  163. package/runtime/sdk/runtime/appEvents.ts +0 -54
  164. package/runtime/sdk/runtime/channel.ts +0 -61
  165. package/runtime/sdk/runtime/clipboard.ts +0 -20
  166. package/runtime/sdk/runtime/datetime.ts +0 -329
  167. package/runtime/sdk/runtime/device.ts +0 -293
  168. package/runtime/sdk/runtime/fetch.ts +0 -77
  169. package/runtime/sdk/runtime/files.ts +0 -23
  170. package/runtime/sdk/runtime/input.ts +0 -175
  171. package/runtime/sdk/runtime/media.ts +0 -111
  172. package/runtime/sdk/runtime/misc.ts +0 -16
  173. package/runtime/sdk/runtime/net.ts +0 -36
  174. package/runtime/sdk/runtime/rpc.ts +0 -218
  175. package/runtime/sdk/runtime/service.ts +0 -83
  176. package/runtime/sdk/runtime/share.ts +0 -9
  177. package/runtime/sdk/runtime/storage.ts +0 -13
  178. package/runtime/sdk/runtime/touch.ts +0 -76
  179. package/runtime/sdk/scene/defineScene.ts +0 -1227
  180. package/runtime/sdk/scene/editorPlugins.ts +0 -92
  181. package/runtime/sdk/scene/gizmos.ts +0 -148
  182. package/runtime/sdk/scene/grammar.ts +0 -120
  183. package/runtime/sdk/scene/material.ts +0 -188
  184. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  185. package/runtime/sdk/server/auth/global.ts +0 -80
  186. package/runtime/sdk/server/auth/host.ts +0 -318
  187. package/runtime/sdk/server/auth/models.ts +0 -83
  188. package/runtime/sdk/server/auth/types.ts +0 -50
  189. package/runtime/sdk/server/channel.ts +0 -56
  190. package/runtime/sdk/server/context.ts +0 -36
  191. package/runtime/sdk/server/db/defineDb.ts +0 -237
  192. package/runtime/sdk/server/db/fields.ts +0 -132
  193. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  194. package/runtime/sdk/server/db/index.ts +0 -7
  195. package/runtime/sdk/server/db/marci/query.ts +0 -412
  196. package/runtime/sdk/server/db/types.ts +0 -202
  197. package/runtime/sdk/server/errors.ts +0 -12
  198. package/runtime/sdk/server/host.ts +0 -74
  199. package/runtime/sdk/server/inject.ts +0 -13
  200. package/runtime/sdk/server/runtime.ts +0 -133
  201. package/runtime/sdk/server/validate.ts +0 -87
  202. package/runtime/sdk/ui/NativeView.ts +0 -142
  203. package/runtime/sdk/ui/UI.ts +0 -39
  204. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  205. package/runtime/sdk/ui/UIButton.ts +0 -101
  206. package/runtime/sdk/ui/UIContainer.ts +0 -60
  207. package/runtime/sdk/ui/UIImage.ts +0 -83
  208. package/runtime/sdk/ui/UIInput.ts +0 -185
  209. package/runtime/sdk/ui/UIModal.ts +0 -139
  210. package/runtime/sdk/ui/UINode.ts +0 -826
  211. package/runtime/sdk/ui/UIPager.ts +0 -362
  212. package/runtime/sdk/ui/UIPopover.ts +0 -100
  213. package/runtime/sdk/ui/UIScreen.ts +0 -123
  214. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  215. package/runtime/sdk/ui/UISpacer.ts +0 -14
  216. package/runtime/sdk/ui/UITabs.ts +0 -236
  217. package/runtime/sdk/ui/UIText.ts +0 -51
  218. package/runtime/sdk/ui/UIVideo.ts +0 -88
  219. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  220. package/runtime/sdk/ui/UIWidget.ts +0 -127
  221. package/runtime/sdk/ui/fonts.ts +0 -13
  222. package/runtime/sdk/ui/presentable.ts +0 -117
  223. package/runtime/sdk/ui/router.ts +0 -132
  224. package/runtime/sdk/ui/theme.ts +0 -84
@@ -1,101 +0,0 @@
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 } from "./UINode"
4
-
5
- export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & { rippleColor?: Color | "default" }
6
-
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 } }, DrawableStyle & BaseStyle> {
10
- readonly type: "button",
11
-
12
- /** Fired on a completed tap/click. */
13
- onClick(callback: (ev: ClickEventType<UIButton>) => void): this,
14
- /** Fired when a touch begins; `ev.track(...)` takes over the rest of the gesture (dragging). */
15
- onTouchStart(callback: (ev: TouchStartEventType<UIButton>) => void): this,
16
- /** Fired when a finger is held on the button past the long-press threshold. The event can
17
- * `ev.track(...)` the rest of the gesture (like `onTouchStart`), so a hold can flow straight into
18
- * dragging the element. A handled long-press suppresses the click that would follow the release. */
19
- onLongPress(callback: (ev: LongPressEventType<UIButton>) => void): this,
20
- /** Is a finger currently down on the button? */
21
- isPressed(): boolean,
22
- }
23
-
24
-
25
- class ButtonElement extends ContainerElement<"button"> {
26
-
27
- readonly clickListeners: any[] = []
28
- readonly touchStartListeners: any[] = []
29
- readonly longPressListeners: any[] = []
30
-
31
- onClick (callback: any): this {
32
- this.clickListeners.push(callback)
33
- return this
34
- }
35
- _emitClick(pointerId: number, clientX: number, clientY: number) {
36
- const touchEvent = new ClickEvent(clientX, clientY, pointerId)
37
- touchEvent.target = this
38
- for (let callback of this.clickListeners) {
39
- callback(touchEvent)
40
- }
41
- return null
42
- }
43
-
44
- onTouchStart (callback: any): this {
45
- this.touchStartListeners.push(callback)
46
- return this
47
- }
48
- _emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
49
- const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
50
- touchEvent.target = this
51
- for (let callback of this.touchStartListeners) {
52
- callback(touchEvent)
53
- if ((touchEvent as any)._trackHandler) {
54
- return (touchEvent as any)._trackHandler
55
- }
56
- }
57
- return null
58
- }
59
-
60
- onLongPress (callback: any): this {
61
- this.longPressListeners.push(callback)
62
- return this
63
- }
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.
68
- _emitLongPress(pointerId: number, clientX: number, clientY: number): any {
69
- if (this.longPressListeners.length === 0) return null
70
- const longPressEvent = new LongPressEvent(clientX, clientY, pointerId)
71
- longPressEvent.target = this
72
- for (let callback of this.longPressListeners) {
73
- callback(longPressEvent)
74
- }
75
- return { track: (longPressEvent as any)._trackHandler ?? null }
76
- }
77
-
78
- isPressed(): boolean {
79
- if (!this._id) return false
80
- return _creatorUI.isButtonPressed(this._id)
81
- }
82
- }
83
-
84
- // Buttons are row + centered by default: the icon+label button is the norm, and yoga's column
85
- // default was a recurring authoring/codegen trap.
86
- const BUTTON_DEFAULTS = { flexDirection: "row", justifyContent: "center", alignItems: "center" } as const
87
-
88
- const makeButton = (style: any, children: any) => new ButtonElement("button", style, children)
89
-
90
- /** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
91
- export const __UIButton = (children: UINodeChild[] | ChildrenFn): UIButton => makeButton({ ...BUTTON_DEFAULTS }, children)
92
-
93
- /** Create a button (row + centered by default — the icon+label shape). Same argument forms as
94
- * `UIColumn`; attach behavior with `.onClick(...)`. */
95
- export function UIButton(...children: UIChildArg[]): UIButton;
96
- 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
- export function UIButton(...args: any[]): UIButton {
100
- return buildUI(args, BUTTON_DEFAULTS, makeButton)
101
- }
@@ -1,60 +0,0 @@
1
- import { ContainerElement, buildUI, type ChildrenFn, type ContainerStyle, type DrawableStyle, type ElementStyle, type UIChildArg, type UIContainerBase, type UINodeChild } from "./UINode"
2
-
3
- export type UIContainerStyle = ElementStyle & DrawableStyle & ContainerStyle
4
-
5
- /** A vertical flex container — children stack top to bottom. */
6
- export interface UIColumn extends UIContainerBase<UIContainerStyle> {
7
- readonly type: "column",
8
- }
9
- const makeColumn = (style: any, children: any) => new ContainerElement("column", style, children)
10
-
11
- /** @internal Compiler fast path (chisel `flatten_ui` raw lowering): construction with a proven
12
- * plain-children array — or a reactive ChildrenFn (the Element constructor branches on
13
- * `typeof === "function"` either way) — skipping the buildUI argument dispatch entirely. */
14
- export const __UIColumn = (children: UINodeChild[] | ChildrenFn): UIColumn => makeColumn({}, children)
15
- export const __UIRow = (children: UINodeChild[] | ChildrenFn): UIRow => makeRow({ flexDirection: "row" }, children)
16
- export const __UIBox = (children: UINodeChild[] | ChildrenFn): UIBox => makeBox({ ...BOX_DEFAULTS }, children)
17
-
18
- /** Create a vertical flex container. Children are variadic — arrays flatten one level, falsy
19
- * entries are skipped; a lone function makes the children reactive. */
20
- export function UIColumn(...children: UIChildArg[]): UIColumn
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
- export function UIColumn(...args: any[]): UIColumn {
25
- return buildUI(args, null, makeColumn)
26
- }
27
-
28
- /** A horizontal flex container — children flow left to right. */
29
- export interface UIRow extends UIContainerBase<UIContainerStyle> {
30
- readonly type: "row",
31
- }
32
- const ROW_DEFAULTS = { flexDirection: "row" } as const
33
- const makeRow = (style: any, children: any) => new ContainerElement("row", style, children)
34
-
35
- /** Create a horizontal flex container (same argument forms as `UIColumn`). */
36
- export function UIRow(...children: UIChildArg[]): UIRow
37
- 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
- export function UIRow(...args: any[]): UIRow {
41
- return buildUI(args, ROW_DEFAULTS, makeRow)
42
- }
43
-
44
- /** A centering container — `justifyContent`/`alignItems` default to center. */
45
- export interface UIBox extends UIContainerBase<UIContainerStyle> {
46
- readonly type: "box",
47
- }
48
- const BOX_DEFAULTS = { justifyContent: "center", alignItems: "center" } as const
49
-
50
- const makeBox = (style: any, children: any) => new ContainerElement("box", style, children)
51
-
52
- /** Create a centering container — children are centered on both axes by default (icon badges,
53
- * avatars, empty states). Same argument forms as `UIColumn`. */
54
- export function UIBox(...children: UIChildArg[]): UIBox
55
- 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
- export function UIBox(...args: any[]): UIBox {
59
- return buildUI(args, BOX_DEFAULTS, makeBox)
60
- }
@@ -1,83 +0,0 @@
1
- import type { FetchResponse } from "../runtime/fetch"
2
- import type { SvgSourceValue as SvgSource } from "../runtime/misc"
3
- import type { File } from "../runtime/fetch"
4
- import { Canvas, isCanvas } from "../canvas/Canvas"
5
- import { Element, type Color, type ElementStyle, type UIElementBase } from "./UINode"
6
-
7
- export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "cover" | "contain" | "fill", tintColor?: Color }
8
-
9
- /** A baked Canvas surface as an image source. The UI backend resolves it to a UI texture — see
10
- * docs/canvas-contract.md ("Pending"). */
11
- export type CanvasImageSource = { canvasSurface: number }
12
-
13
- /** Accepted image sources. A Canvas is baked to a surface marker on assign. */
14
- export type ImageSource = string | FetchResponse | File | SvgSource | Canvas
15
-
16
- const toSrc = (src: ImageSource): string | FetchResponse | File | SvgSource | CanvasImageSource =>
17
- // _bakeSurfaceForUI (not _bakeSurface) also flags the canvas UI-bound, so canvas.update() later
18
- // refreshes this image node in place — that's what makes an animated UIImage(canvas) update.
19
- isCanvas(src) ? { canvasSurface: src._bakeSurfaceForUI() } : src
20
-
21
- /** The stored/resolved image source (a Canvas is resolved to a surface marker). */
22
- export type ResolvedImageSource = string | FetchResponse | File | SvgSource | CanvasImageSource
23
-
24
- /** An image element. Sources: URL / `asset(...)`, fetch response / File, an SVG source
25
- * (`assetIcon`), or a live Canvas (`canvas.update()` refreshes the node in place). */
26
- export interface UIImage extends UIElementBase<UIImageStyle> {
27
- readonly type: "image",
28
- /** The image source — writable to swap the picture in place. */
29
- get src(): ResolvedImageSource
30
- set src(value: ImageSource)
31
-
32
- /** Render only a sub-rectangle of the source texture (texture pixels) — the atlas/spritesheet
33
- * primitive: crop once, then swap the rect per frame for sprite animation. The rect's w/h
34
- * become the node's intrinsic size and override `objectFit` — the cropped frame fills the box. */
35
- setSourceRect(x: number, y: number, w: number, h: number): this
36
- }
37
-
38
- class ImageElement extends Element<"image"> {
39
- private _src: ResolvedImageSource
40
- // Current source sub-rect; undefined until the first setSourceRect. Kept on the node (not
41
- // just sent as a command) so a crop set while detached (_id == 0) is reapplied at mount —
42
- // the viewer reads it when it creates the element.
43
- private _sourceRect?: [number, number, number, number]
44
- constructor(style: any, src: ImageSource) {
45
- super("image", style)
46
- this._src = toSrc(src)
47
- }
48
-
49
- get src(): ResolvedImageSource {
50
- return this._src
51
- }
52
- set src(value: ImageSource) {
53
- this._src = toSrc(value)
54
- if (this._id !== 0) {
55
- _creatorUI.updateNode(this._id, "src", this._src)
56
- }
57
- }
58
-
59
- setSourceRect(x: number, y: number, w: number, h: number): this {
60
- // Stash on the node so a crop set before mount is reapplied when the element is created.
61
- this._sourceRect = [x, y, w, h]
62
- // Live mutation: fast channel (not _style → no PX_VALUE density scaling: these are texture
63
- // pixels, a different coordinate space). On a detached node there is nothing to mutate yet —
64
- // the stash above carries it to mount. The viewer decides relayout vs draw-only by comparing
65
- // w/h against the rect it has stashed on the element.
66
- if (this._id !== 0) {
67
- _creatorUI.setSourceRect(this._id, x, y, w, h)
68
- }
69
- return this
70
- }
71
- }
72
-
73
- /** Create an image element from a source (see {@link ImageSource}). */
74
- export function UIImage(src: ImageSource): UIImage
75
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
76
- export function UIImage(style: UIImageStyle, src: ImageSource): UIImage
77
- export function UIImage(...args: [ImageSource] | [UIImageStyle, ImageSource]): UIImage {
78
- if (args.length === 1) {
79
- return new ImageElement({}, args[0])
80
- } else {
81
- return new ImageElement(args[0], args[1])
82
- }
83
- }
@@ -1,185 +0,0 @@
1
- import { type ElementStyle, type DrawableStyle, Element, type TextStyle, type Color, type BaseStyle, type UIElementBase } from "./UINode";
2
-
3
- type InputStyle = {
4
- placeholder?: string,
5
- placeholderColor?: Color,
6
- /**
7
- * Keyboard hint, NOT a validator — `number` does not block pasted letters; validate in your own
8
- * onChange. The phone-pad value is `phone` (LeCodes has no `tel`).
9
- *
10
- * `date` / `time` are PICKER kinds, not keyboards: the host opens its native picker in the
11
- * keyboard slot (iOS wheels / browser calendar) and free typing is disabled. `value` is always
12
- * canonical — `YYYY-MM-DD` for `date`, `HH:MM` (24h) for `time` — while the field displays a
13
- * localized string; set/compare values in the canonical form only.
14
- */
15
- type?: "text" | "password" | "search" | "phone" | "email" | "number" | "decimal" | "url"
16
- | "date" | "time",
17
- /**
18
- * What the layout does while the keyboard is up (decided by the FOCUSED input). `true` (default):
19
- * the layout viewport shrinks to the area above the keyboard — one relayout. `false`: the keyboard
20
- * overlays the UI with NO layout recalculation — for chat composers that manage their own inset
21
- * (pair with `app.keyboardHeight` / the app `"keyboard"` event) and full-screen canvases where a
22
- * reflow is worse than overlap.
23
- */
24
- keyboardShrink?: boolean,
25
- /**
26
- * Whether a tap on a non-interactive area dismisses the keyboard while THIS input is focused
27
- * (like `keyboardShrink`, the policy of the focused input). `true` (default): tapping dead
28
- * space blurs — but a tap on a button or another input NEVER dismisses; the control acts and
29
- * focus stays. `false`: taps never dismiss — the chat-composer policy. `blur()`, the return
30
- * key (per `enterKey`), and the platform's own gestures (iOS drag-down over the keyboard)
31
- * still work in both modes. Scrolling never dismisses the keyboard on any setting.
32
- */
33
- keyboardDismiss?: boolean,
34
- /** Hard cap on the text length — enforced by the host while typing/pasting. */
35
- maxLength?: number,
36
- /** Auto-capitalization hint; omitted = platform default. */
37
- autocapitalize?: "none" | "words" | "sentences" | "characters",
38
- /** Enable/disable the platform autocorrection & suggestions; omitted = platform default. */
39
- autocorrect?: boolean,
40
- }
41
- export type UIInputStyle = ElementStyle & DrawableStyle & TextStyle & InputStyle
42
-
43
- /** UIInput-only (a UITextArea's Enter is a newline, period): the return-key label. */
44
- type SubmitStyle = {
45
- /**
46
- * Return-key label. On submit the keyboard dismisses UNLESS `enterKey` is `"next"` — then the
47
- * app is expected to call `otherInput.focus()` from `onSubmit`, so the keyboard never blinks.
48
- */
49
- enterKey?: "done" | "go" | "next" | "search" | "send",
50
- }
51
-
52
- /** A single-line text input. Keyboard/picker kind via the `type` style, return-key behavior via
53
- * `enterKey` + `onSubmit`; keyboard policy on `keyboardShrink`/`keyboardDismiss`. */
54
- export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle & { onFocused?: UIInputStyle & { duration?: number } }, DrawableStyle & BaseStyle> {
55
- readonly type: "input",
56
-
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
- */
63
- value: string
64
-
65
- /** The text changed — fires on every user edit. */
66
- onChange(callback: (value: string) => void): UIInput
67
- /** Gained focus (keyboard opens). */
68
- onFocus(callback: () => void): UIInput
69
- /** Lost focus. */
70
- onBlur(callback: () => void): UIInput
71
- /** The keyboard's return key was pressed. `value` is the current text. See `enterKey`. */
72
- onSubmit(callback: (value: string) => void): UIInput
73
-
74
- /** Focus the input (opens the keyboard). No-op before mount. */
75
- focus(): void
76
- /** Release focus (dismisses the keyboard when this input held it). */
77
- blur(): void
78
- }
79
-
80
- /** 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 } }, DrawableStyle & BaseStyle> {
82
- readonly type: "textarea",
83
-
84
- /** The field's text — same contract as {@link UIInput.value}: settable before mount to pre-fill. */
85
- value: string
86
-
87
- /** The text changed — fires on every user edit. */
88
- onChange(callback: (value: string) => void): UITextArea
89
- /** Gained focus (keyboard opens). */
90
- onFocus(callback: () => void): UITextArea
91
- /** Lost focus. */
92
- onBlur(callback: () => void): UITextArea
93
-
94
- /** Focus the text area (opens the keyboard). No-op before mount. */
95
- focus(): void
96
- /** Release focus (dismisses the keyboard when this text area held it). */
97
- blur(): void
98
- }
99
-
100
- class InputElement<T extends "input" | "textarea"> extends Element<T> {
101
-
102
- readonly cl: Function[] = []
103
- readonly fl: Function[] = []
104
- readonly bl: Function[] = []
105
- readonly sl: Function[] = []
106
-
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
120
- }
121
- set value(value: string) {
122
- this._value = value
123
- if (this._id !== 0) {
124
- _creatorUI.updateNode(this._id, "value", value)
125
- }
126
- }
127
-
128
- // `phone` is SDK-surface only — hosts (and the web's verbatim `el.type`) keep HTML's `tel` as
129
- // the wire format, so the rename never reaches them.
130
- _mapStyle(style: any): void {
131
- if (style.type === "phone") style.type = "tel"
132
- }
133
-
134
- onChange (callback: (value: string) => void): this {
135
- this.cl.push(callback)
136
- return this
137
- }
138
- onFocus(callback: () => void): this {
139
- this.fl.push(callback)
140
- return this
141
- }
142
- onBlur(callback: () => void): this {
143
- this.bl.push(callback)
144
- return this
145
- }
146
- onSubmit(callback: (value: string) => void): this {
147
- this.sl.push(callback)
148
- return this
149
- }
150
-
151
- // Focus rides the existing value channel (updateNode "focus") — no new ABI method.
152
- focus(): void {
153
- if (this._id !== 0) {
154
- _creatorUI.updateNode(this._id, "focus", true)
155
- }
156
- }
157
- blur(): void {
158
- if (this._id !== 0) {
159
- _creatorUI.updateNode(this._id, "focus", false)
160
- }
161
- }
162
- }
163
-
164
- /** Create a single-line text input. Read/write the text via `.value`, listen with
165
- * `onChange`/`onSubmit`; style keys `placeholder`, `type`, `enterKey`, … configure it. */
166
- export function UIInput(): UIInput;
167
- export function UIInput(style: UIInputStyle): UIInput;
168
- export function UIInput(style?: UIInputStyle): UIInput {
169
- // One text line as the floor — an unstyled input must never collapse to 0 height.
170
- return new InputElement("input", { minHeight: "1.2em", ...style })
171
- }
172
-
173
- /**
174
- * Multi-line text field. Enter is a newline (submit lives on UIInput / your own button).
175
- *
176
- * Auto-grow: with no explicit `height` the field measures its own content and grows line by
177
- * line — clamp the range with `minHeight` / `maxHeight` (past `maxHeight` the text scrolls
178
- * inside). A fixed `height` opts out and scrolls internally from the start. Chat composers are
179
- * the canonical use: a bare textarea in a padded pill, `maxHeight` at ~5 lines.
180
- */
181
- export function UITextArea(): UITextArea;
182
- export function UITextArea(style: UIInputStyle): UITextArea;
183
- export function UITextArea(style?: UIInputStyle): UITextArea {
184
- return new InputElement("textarea", { minHeight: "1.2em", ...style })
185
- }
@@ -1,139 +0,0 @@
1
- import { type UIWidget, type UIWidgetStyle, WidgetElement } from "./UIWidget"
2
- import { buildUI, type BaseStyle, type ChildrenFn, type Color, type DrawableStyle, type UIChildArg, type UINodeChild } from "./UINode"
3
-
4
- export type UIModalStyle = UIWidgetStyle
5
-
6
- /**
7
- * The modal's hidden pose: `show()` animates from it, `hide()` animates to it. `duration` (ms)
8
- * applies to both directions.
9
- */
10
- export type UIModalTransition = DrawableStyle & BaseStyle & {
11
- overlayColor?: Color | null,
12
- duration?: number,
13
- }
14
-
15
- const MODAL_ANIM_MS = 200
16
-
17
- /**
18
- * A dialog: a `UIWidget` with the modal boilerplate built in. Comes with a scrim
19
- * (`overlayColor: "rgba(0, 0, 0, 0.5)"` unless overridden), animates on `show()`/`hide()`
20
- * (a 200 ms fade by default — swap the pose via `transition()`; the exit plays with
21
- * `commit: false`, so the style is intact for the next show), and closes itself on a scrim tap
22
- * or the Android back button (disable via `dismissible(false)`). Everything else is a plain
23
- * widget — same styling, children, touch and `attachTo()` surface; create it **once** at module
24
- * scope and reuse it.
25
- */
26
- export interface UIModal extends UIWidget {
27
- /** Mount as an overlay and play the entrance transition. No-op while already open. */
28
- show(): void,
29
- /** Play the exit transition, then unmount. No-op while closed or already closing. */
30
- hide(): void,
31
- /** True from `show()` until `hide()` starts (already false during the exit animation). */
32
- readonly isOpen: boolean,
33
-
34
- /**
35
- * Replace the show/hide animation. `hidden` is the off-screen pose — `show()` animates from
36
- * it, `hide()` animates to it. Default `{ opacity: 0, duration: 200 }` (a fade); the pose
37
- * replaces it wholesale, so a slide without a fade is just the slide. The scrim keeps its own
38
- * fade unless the pose sets `overlayColor` itself.
39
- *
40
- * ```ts
41
- * sheet.transition({ transform: "translateY(480px)", duration: 250 }) // bottom sheet
42
- * ```
43
- */
44
- transition(hidden: UIModalTransition): this,
45
-
46
- /** Called when `show()` mounts the modal. */
47
- onOpen(callback: () => void): this,
48
- /** Called when the modal starts closing — scrim tap, back button, or a `hide()` call. */
49
- onClose(callback: () => void): this,
50
- /** `dismissible(false)` keeps scrim taps and the back button from closing the modal
51
- * (forced-choice dialogs). Default `true`. */
52
- dismissible(enabled: boolean): this,
53
- }
54
-
55
- export class ModalElement extends WidgetElement {
56
- _open = false
57
- _dismissible = true
58
- _transition: UIModalTransition = { opacity: 0 }
59
- _closeTimer?: ReturnType<typeof setTimeout>
60
- readonly _openListeners: (() => void)[] = []
61
- readonly _closeListeners: (() => void)[] = []
62
-
63
- constructor(style: UIModalStyle, children: UINodeChild[] | ChildrenFn) {
64
- super("widget", { overlayColor: "rgba(0, 0, 0, 0.5)", ...style }, children)
65
- this.onOverlayTap(() => { if (this._dismissible) this.hide() })
66
- this.onBackPressed(() => { if (this._dismissible) this.hide() })
67
- }
68
-
69
- // The full animate argument: hidden pose + duration, plus the scrim fade — only when the
70
- // modal actually has a scrim (overlayColor: null opts out; animating a null scrim would
71
- // create the intercepting layer) and the pose doesn't drive the scrim itself.
72
- private _pose(extra?: Record<string, unknown>): Record<string, unknown> {
73
- const pose: Record<string, unknown> = { duration: MODAL_ANIM_MS, ...this._transition, ...extra }
74
- const overlayColor = (this._style as UIModalStyle).overlayColor
75
- if (pose.overlayColor === undefined && overlayColor !== undefined && overlayColor !== null) {
76
- pose.overlayColor = "transparent"
77
- }
78
- return pose
79
- }
80
-
81
- private get _duration(): number {
82
- return this._transition.duration ?? MODAL_ANIM_MS
83
- }
84
-
85
- transition(hidden: UIModalTransition): this {
86
- this._transition = hidden
87
- return this
88
- }
89
-
90
- show(): void {
91
- if (this._open) return
92
- if (this._closeTimer !== undefined) { // reopened mid exit: finish that hide now
93
- clearTimeout(this._closeTimer)
94
- this._closeTimer = undefined
95
- super.hide()
96
- }
97
- this._open = true
98
- super.show()
99
- this.animateFrom(this._pose())
100
- for (const callback of this._openListeners) callback()
101
- }
102
-
103
- hide(): void {
104
- if (!this._open) return
105
- this._open = false
106
- this.animateTo(this._pose({ commit: false }))
107
- this._closeTimer = setTimeout(() => { this._closeTimer = undefined; super.hide() }, this._duration)
108
- for (const callback of this._closeListeners) callback()
109
- }
110
-
111
- get isOpen(): boolean {
112
- return this._open
113
- }
114
-
115
- onOpen(callback: () => void): this {
116
- this._openListeners.push(callback)
117
- return this
118
- }
119
- onClose(callback: () => void): this {
120
- this._closeListeners.push(callback)
121
- return this
122
- }
123
- dismissible(enabled: boolean): this {
124
- this._dismissible = enabled
125
- return this
126
- }
127
- }
128
-
129
- const makeModal = (style: any, children: any) => new ModalElement(style, children)
130
-
131
- /** Create a modal dialog (see {@link UIModal}) — create once at module scope, then
132
- * `show()`/`hide()`. Same argument forms as `UIColumn`. */
133
- export function UIModal(...children: UIChildArg[]): UIModal;
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
- export function UIModal(...args: any[]): UIModal {
138
- return buildUI(args, null, makeModal)
139
- }