lecodes-cli 0.19.0 → 0.20.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 (270) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1069 -396
  3. package/package.json +14 -13
  4. package/runtime/scene-harness.json +1 -1
  5. package/runtime/sdk-types.json +1 -1
  6. package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  7. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  8. package/runtime/web/assets/basis-C64VHDVD.js +1 -0
  9. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  10. package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
  11. package/runtime/web/assets/draco-BiISTFcR.js +118 -0
  12. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  13. package/runtime/web/assets/index-CfNCCBjN.js +2 -0
  14. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
  15. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
  16. package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  17. package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
  18. package/runtime/web/embed.html +32 -0
  19. package/runtime/web/embed.js +171 -0
  20. package/src/api.ts +302 -0
  21. package/src/browserAuth.ts +87 -0
  22. package/src/cmgenTool.ts +104 -0
  23. package/src/commands/app.ts +896 -0
  24. package/src/commands/appAndroid.ts +603 -0
  25. package/src/commands/appDesktop.ts +339 -0
  26. package/src/commands/appDesktopMac.ts +470 -0
  27. package/src/commands/appIcon.ts +187 -0
  28. package/src/commands/appShared.ts +542 -0
  29. package/src/commands/appTemplates.ts +907 -0
  30. package/src/commands/appTemplatesAndroid.ts +599 -0
  31. package/src/commands/appTemplatesGradlew.ts +9 -0
  32. package/src/commands/assets.ts +28 -0
  33. package/src/commands/clone.ts +60 -0
  34. package/src/commands/compile.ts +253 -0
  35. package/src/commands/create.ts +59 -0
  36. package/src/commands/design.ts +863 -0
  37. package/src/commands/designTemplates.ts +11 -0
  38. package/src/commands/desktop.ts +127 -0
  39. package/src/commands/dev.ts +236 -0
  40. package/src/commands/diff.ts +59 -0
  41. package/src/commands/init.ts +191 -0
  42. package/src/commands/install.ts +198 -0
  43. package/src/commands/lightmap.ts +290 -0
  44. package/src/commands/link.ts +147 -0
  45. package/src/commands/login.ts +24 -0
  46. package/src/commands/navmesh.ts +225 -0
  47. package/src/commands/pn.ts +305 -0
  48. package/src/commands/projectTemplates.ts +130 -0
  49. package/src/commands/pull.ts +109 -0
  50. package/src/commands/push.ts +173 -0
  51. package/src/commands/render.ts +416 -0
  52. package/src/commands/scene.ts +148 -0
  53. package/src/commands/shaders.ts +157 -0
  54. package/src/commands/status.ts +29 -0
  55. package/src/commands/test.ts +371 -0
  56. package/src/commands/thumbs.ts +178 -0
  57. package/src/commands/types.ts +87 -0
  58. package/src/commands/update.ts +190 -0
  59. package/src/compile/assetIcons.ts +214 -0
  60. package/src/compile/collect.ts +46 -0
  61. package/src/compile/collectLocal.ts +37 -0
  62. package/src/compile/designCompile.ts +104 -0
  63. package/src/compile/fonts.ts +156 -0
  64. package/src/compile/headlessBundle.ts +129 -0
  65. package/src/compile/nativeStack.ts +21 -0
  66. package/src/compile/projectCompile.ts +109 -0
  67. package/src/compile/sceneCompile.ts +113 -0
  68. package/src/compile/screenEntry.ts +127 -0
  69. package/src/compile/shaders.ts +245 -0
  70. package/src/config.ts +42 -0
  71. package/src/designMeta.ts +35 -0
  72. package/src/desktopRenderer.ts +545 -0
  73. package/src/desktopScript.ts +276 -0
  74. package/src/dev/clientTemplates.ts +160 -0
  75. package/src/dev/devServer.ts +333 -0
  76. package/src/dev/webRunner.ts +22 -0
  77. package/src/dev/wsServer.ts +144 -0
  78. package/src/distRoot.ts +20 -0
  79. package/src/ignore.ts +163 -0
  80. package/src/index.ts +494 -0
  81. package/src/lecodes-3d-editor.d.ts +41 -0
  82. package/src/lecodes-assets.d.ts +7 -0
  83. package/src/lecodes-design.d.ts +191 -0
  84. package/src/lecodes-renderer.d.ts +131 -0
  85. package/src/localFiles.ts +144 -0
  86. package/src/manifest.ts +46 -0
  87. package/src/matcTool.ts +137 -0
  88. package/src/peers.ts +40 -0
  89. package/src/project.ts +61 -0
  90. package/src/projectEnv.ts +94 -0
  91. package/src/qrcode-terminal.d.ts +9 -0
  92. package/src/releases.ts +125 -0
  93. package/src/serverDiff.ts +78 -0
  94. package/src/textDiff.ts +103 -0
  95. package/src/types.ts +0 -0
  96. package/src/util.ts +146 -0
  97. package/runtime/sdk/animate/animate.ts +0 -238
  98. package/runtime/sdk/animate/bezier.ts +0 -138
  99. package/runtime/sdk/animate/easings.ts +0 -126
  100. package/runtime/sdk/canvas/Canvas.ts +0 -305
  101. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  102. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  103. package/runtime/sdk/compile/assetMacro.ts +0 -146
  104. package/runtime/sdk/compile/assetName.ts +0 -50
  105. package/runtime/sdk/compile/bundler.ts +0 -287
  106. package/runtime/sdk/compile/compileProject.ts +0 -129
  107. package/runtime/sdk/compile/detectEntry.ts +0 -128
  108. package/runtime/sdk/compile/fontMacro.ts +0 -459
  109. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  110. package/runtime/sdk/compile/header.ts +0 -67
  111. package/runtime/sdk/compile/index.ts +0 -100
  112. package/runtime/sdk/compile/libraryImports.ts +0 -52
  113. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  114. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  115. package/runtime/sdk/compile/serverSplit.ts +0 -233
  116. package/runtime/sdk/compile/serverTypes.ts +0 -227
  117. package/runtime/sdk/compile/sfnt.ts +0 -98
  118. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  119. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  120. package/runtime/sdk/compile/sourcemap.ts +0 -25
  121. package/runtime/sdk/core/Aspect.ts +0 -568
  122. package/runtime/sdk/core/InspectorUI.ts +0 -212
  123. package/runtime/sdk/core/StateMachine.ts +0 -308
  124. package/runtime/sdk/core/color.ts +0 -66
  125. package/runtime/sdk/core/compWrite.ts +0 -42
  126. package/runtime/sdk/core/events.ts +0 -38
  127. package/runtime/sdk/core/fields.ts +0 -120
  128. package/runtime/sdk/core/registry.ts +0 -23
  129. package/runtime/sdk/core/signals.ts +0 -277
  130. package/runtime/sdk/core/time.ts +0 -84
  131. package/runtime/sdk/g2/Camera2D.ts +0 -40
  132. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  133. package/runtime/sdk/g2/Node2D.ts +0 -267
  134. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  135. package/runtime/sdk/g2/Physics2D.ts +0 -346
  136. package/runtime/sdk/g2/Scene2D.ts +0 -209
  137. package/runtime/sdk/g2/Shape2D.ts +0 -259
  138. package/runtime/sdk/g2/Sprite.ts +0 -89
  139. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  140. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  141. package/runtime/sdk/g2/Texture2D.ts +0 -47
  142. package/runtime/sdk/g2/Tilemap.ts +0 -41
  143. package/runtime/sdk/g2/Tileset.ts +0 -71
  144. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  145. package/runtime/sdk/g2/autotile.ts +0 -433
  146. package/runtime/sdk/g2/cells.ts +0 -91
  147. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  148. package/runtime/sdk/g2/groups2d.ts +0 -106
  149. package/runtime/sdk/g2/loop.ts +0 -50
  150. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  151. package/runtime/sdk/g2/touch.ts +0 -83
  152. package/runtime/sdk/gl/Camera.ts +0 -160
  153. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  154. package/runtime/sdk/gl/CharacterController.ts +0 -238
  155. package/runtime/sdk/gl/Gearbox.ts +0 -212
  156. package/runtime/sdk/gl/Geometry.ts +0 -279
  157. package/runtime/sdk/gl/IK.ts +0 -193
  158. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  159. package/runtime/sdk/gl/Light.ts +0 -107
  160. package/runtime/sdk/gl/Lightmap.ts +0 -256
  161. package/runtime/sdk/gl/Material.ts +0 -260
  162. package/runtime/sdk/gl/Mesh.ts +0 -83
  163. package/runtime/sdk/gl/Model.ts +0 -124
  164. package/runtime/sdk/gl/NavAgent.ts +0 -337
  165. package/runtime/sdk/gl/NavMesh.ts +0 -397
  166. package/runtime/sdk/gl/Node.ts +0 -350
  167. package/runtime/sdk/gl/Noise.ts +0 -30
  168. package/runtime/sdk/gl/Particles.ts +0 -676
  169. package/runtime/sdk/gl/Physics.ts +0 -222
  170. package/runtime/sdk/gl/Plane.ts +0 -53
  171. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  172. package/runtime/sdk/gl/Ray.ts +0 -16
  173. package/runtime/sdk/gl/Scene.ts +0 -486
  174. package/runtime/sdk/gl/Shape.ts +0 -409
  175. package/runtime/sdk/gl/Terrain.ts +0 -1092
  176. package/runtime/sdk/gl/Texture.ts +0 -63
  177. package/runtime/sdk/gl/Trigger.ts +0 -45
  178. package/runtime/sdk/gl/Vehicle.ts +0 -473
  179. package/runtime/sdk/gl/Wheel.ts +0 -240
  180. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  181. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  182. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  183. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  184. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  185. package/runtime/sdk/gl/animation/core.ts +0 -336
  186. package/runtime/sdk/gl/controls.ts +0 -95
  187. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  188. package/runtime/sdk/gl/scenarios.ts +0 -291
  189. package/runtime/sdk/gl/state.ts +0 -6
  190. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  191. package/runtime/sdk/gl/touch.ts +0 -68
  192. package/runtime/sdk/inject.ts +0 -197
  193. package/runtime/sdk/math/Mathf.ts +0 -118
  194. package/runtime/sdk/math/mat4.ts +0 -278
  195. package/runtime/sdk/math/quat.ts +0 -232
  196. package/runtime/sdk/math/vec.ts +0 -255
  197. package/runtime/sdk/net/codec.ts +0 -119
  198. package/runtime/sdk/net/core.ts +0 -384
  199. package/runtime/sdk/net/index.ts +0 -181
  200. package/runtime/sdk/net/replication.ts +0 -622
  201. package/runtime/sdk/plugins/camera.ts +0 -81
  202. package/runtime/sdk/plugins/geolocation.ts +0 -123
  203. package/runtime/sdk/plugins/oauth.ts +0 -61
  204. package/runtime/sdk/plugins/permission.ts +0 -7
  205. package/runtime/sdk/plugins/push.ts +0 -132
  206. package/runtime/sdk/plugins/qr.ts +0 -73
  207. package/runtime/sdk/plugins/service.ts +0 -47
  208. package/runtime/sdk/runtime/app.ts +0 -101
  209. package/runtime/sdk/runtime/appEvents.ts +0 -54
  210. package/runtime/sdk/runtime/channel.ts +0 -61
  211. package/runtime/sdk/runtime/clipboard.ts +0 -20
  212. package/runtime/sdk/runtime/datetime.ts +0 -329
  213. package/runtime/sdk/runtime/device.ts +0 -293
  214. package/runtime/sdk/runtime/fetch.ts +0 -77
  215. package/runtime/sdk/runtime/files.ts +0 -23
  216. package/runtime/sdk/runtime/input.ts +0 -175
  217. package/runtime/sdk/runtime/media.ts +0 -111
  218. package/runtime/sdk/runtime/misc.ts +0 -16
  219. package/runtime/sdk/runtime/net.ts +0 -36
  220. package/runtime/sdk/runtime/rpc.ts +0 -218
  221. package/runtime/sdk/runtime/service.ts +0 -83
  222. package/runtime/sdk/runtime/share.ts +0 -9
  223. package/runtime/sdk/runtime/storage.ts +0 -13
  224. package/runtime/sdk/runtime/touch.ts +0 -76
  225. package/runtime/sdk/scene/defineScene.ts +0 -1353
  226. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  227. package/runtime/sdk/scene/gizmos.ts +0 -148
  228. package/runtime/sdk/scene/grammar.ts +0 -120
  229. package/runtime/sdk/scene/material.ts +0 -188
  230. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  231. package/runtime/sdk/server/auth/global.ts +0 -80
  232. package/runtime/sdk/server/auth/host.ts +0 -318
  233. package/runtime/sdk/server/auth/models.ts +0 -83
  234. package/runtime/sdk/server/auth/types.ts +0 -50
  235. package/runtime/sdk/server/channel.ts +0 -56
  236. package/runtime/sdk/server/context.ts +0 -36
  237. package/runtime/sdk/server/db/defineDb.ts +0 -237
  238. package/runtime/sdk/server/db/fields.ts +0 -132
  239. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  240. package/runtime/sdk/server/db/index.ts +0 -7
  241. package/runtime/sdk/server/db/marci/query.ts +0 -412
  242. package/runtime/sdk/server/db/types.ts +0 -202
  243. package/runtime/sdk/server/errors.ts +0 -12
  244. package/runtime/sdk/server/host.ts +0 -74
  245. package/runtime/sdk/server/inject.ts +0 -13
  246. package/runtime/sdk/server/runtime.ts +0 -133
  247. package/runtime/sdk/server/validate.ts +0 -87
  248. package/runtime/sdk/ui/NativeView.ts +0 -142
  249. package/runtime/sdk/ui/UI.ts +0 -39
  250. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  251. package/runtime/sdk/ui/UIButton.ts +0 -101
  252. package/runtime/sdk/ui/UIContainer.ts +0 -60
  253. package/runtime/sdk/ui/UIImage.ts +0 -83
  254. package/runtime/sdk/ui/UIInput.ts +0 -185
  255. package/runtime/sdk/ui/UIModal.ts +0 -139
  256. package/runtime/sdk/ui/UINode.ts +0 -830
  257. package/runtime/sdk/ui/UIPager.ts +0 -362
  258. package/runtime/sdk/ui/UIPopover.ts +0 -100
  259. package/runtime/sdk/ui/UIScreen.ts +0 -123
  260. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  261. package/runtime/sdk/ui/UISpacer.ts +0 -14
  262. package/runtime/sdk/ui/UITabs.ts +0 -236
  263. package/runtime/sdk/ui/UIText.ts +0 -51
  264. package/runtime/sdk/ui/UIVideo.ts +0 -88
  265. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  266. package/runtime/sdk/ui/UIWidget.ts +0 -127
  267. package/runtime/sdk/ui/fonts.ts +0 -13
  268. package/runtime/sdk/ui/presentable.ts +0 -117
  269. package/runtime/sdk/ui/router.ts +0 -132
  270. package/runtime/sdk/ui/theme.ts +0 -84
@@ -1,362 +0,0 @@
1
- import { ContainerElement, type BaseStyle, type ChildrenFn, type DrawableStyle, type ElementStyle, type UIElementBase, type UINodeChild } from "./UINode"
2
- import { disposeBinding } from "../core/signals"
3
- import type { UIScreen } from "./UIScreen"
4
-
5
- // UIPager — the system's ONE screen-navigation element (docs/pager-component-plan.md): sibling
6
- // tabs that swipe natively side to side, with a native navigation STACK per tab. `push` opens a
7
- // screen on top of the currently open tab (within the pager); `pop` / the interactive edge
8
- // back-swipe unwinds it; `replace` swaps the top. A pager with one tab is a plain stack.
9
- //
10
- // Reachable from any scope as a global: `UIPager.push(screen)` pushes onto the pager that owns
11
- // the current screen (UIPager.current) — no reference threading into child screens. Router stays
12
- // distinct: `Router.push` opens ON TOP of the pager (bars and all), the pager navigates within.
13
- //
14
- // Wire node type "pager": children are separate layout roots (full UIScreens), NOT yoga children.
15
- // The wire shape is `children` flat in TAB-MAJOR order (tab 0's stack root→top, then tab 1's, …)
16
- // partitioned by `_lens` (per-tab stack lengths) with `_index` = selected tab. State is mutated
17
- // BEFORE each command, so the host reconciles against the node; commands only name the native
18
- // transition to play. Host-driven changes come back via `_hostDidSelect` / `_hostDidPop` — both
19
- // idempotent no-ops when the logical state already matches.
20
-
21
- /** Styles a pager accepts — layout box + drawable (background, borders, …). Pages style
22
- * themselves; the pager only owns its own slot. */
23
- export type UIPagerStyle = ElementStyle & DrawableStyle
24
-
25
- /** The pagers whose screen is currently visible, outermost → innermost. `UIPager.current` is the
26
- * last (innermost). Maintained by each owned screen's onOpen/onClose via _activate/_deactivate. */
27
- const _activeStack: PagerElement[] = []
28
-
29
- /**
30
- * The screen-navigation element: sibling **tabs** that swipe natively side to side, each with its
31
- * own navigation **stack**. `push` opens a screen on top of the current tab (within the pager);
32
- * `pop` / the native edge back-swipe unwinds it. A one-tab pager is a plain navigation stack.
33
- *
34
- * ```ts
35
- * const pager = UIPager([ HomeScreen(), ProfileScreen() ]) // two tabs, side by side
36
- * Router.init(UIScreen([ pager.style({ flexGrow: 1 }), tabBar ]))
37
- *
38
- * // from anywhere inside a hosted screen — no reference threading:
39
- * UIPager.push(PostScreen(id))
40
- * ```
41
- *
42
- * To open something over *everything* (above bars, above the pager), use `Router.push` instead.
43
- */
44
- export interface UIPager extends UIElementBase<UIPagerStyle, DrawableStyle & BaseStyle> {
45
- readonly type: "pager",
46
-
47
- /** Replace the pager's tabs — one screen per tab, in order. Every tab becomes a fresh
48
- * one-screen stack (pushed screens are dropped) and the selection is clamped into range;
49
- * screens that were already tabs keep their state. Pass a function for reactive tabs. */
50
- setContent(tabs: UIScreen[] | (() => UIScreen[])): this,
51
-
52
- /** Switch to tab `index`. **Instant by default** — the tab-bar convention (tabs are parallel
53
- * modes, not neighbors); pass `animated: true` for the direction-aware slide of top-tab /
54
- * pager UIs. Out-of-range and same-index calls are no-ops. Tabs keep their stacks: selecting
55
- * away and back restores the tab exactly where it was, pushed screens included. */
56
- select(index: number, animated?: boolean): this,
57
- /** The selected tab index (0-based). */
58
- readonly index: number,
59
- /** Fires when the selected tab changes and has settled — a user swipe or `select()`. */
60
- onSelect(callback: (index: number) => void): this,
61
-
62
- /** Push `screen` on top of the **current tab** — slides in natively; the edge back-swipe (or
63
- * `pop()`) removes it. Other tabs are unaffected. */
64
- push(screen: UIScreen): this,
65
- /** Pop the current tab's top screen (no-op at the tab root — the root never pops). The
66
- * programmatic twin of the native edge back-swipe. */
67
- pop(): this,
68
- /** Unwind the current tab back to its root screen in one step. */
69
- popToRoot(): this,
70
- /** Swap the current tab's top screen for `screen` without growing the stack (at depth 1 this
71
- * swaps the tab's root). */
72
- replace(screen: UIScreen): this,
73
- /** The current tab's screens, root → top (read-only snapshot). */
74
- readonly stack: UIScreen[],
75
- /** The current tab's stack depth — 1 means only the root. While `depth > 1`, swiping between
76
- * tabs is disabled (the horizontal gesture belongs to the back-swipe). */
77
- readonly depth: number,
78
- /** Fires when the current tab's depth changes — push, pop, replace, or a back-swipe. */
79
- onChange(callback: (depth: number) => void): this,
80
- }
81
-
82
- export class PagerElement extends ContainerElement<"pager"> {
83
- /** Per-tab stack lengths — partitions the flat tab-major `children`. Named `_*` so chisel
84
- * keeps it (the host reads it live). */
85
- _lens: number[]
86
- /** Selected tab. */
87
- _index = 0
88
- // >0 while a screen owned by this pager is the visible one (its onOpen fired, onClose hasn't).
89
- // Only one owned screen is ever open, so this is effectively 0/1 — the count only makes the
90
- // transient during a transition's old-close/new-open pair order-independent.
91
- _openCount = 0
92
- private scl: ((index: number) => void)[] = [] // onSelect listeners
93
- private ncl: ((depth: number) => void)[] = [] // onChange listeners
94
-
95
- constructor(style: UIPagerStyle | null, tabs: UIScreen[]) {
96
- // flexShrink: 1 default (SDK-owned, like UIScrollable's): pages are separate layout roots
97
- // sized to the pager's box, so yoga sees no shrinkable content — without this a pager in a
98
- // flex column overflows the screen instead of leaving room for the tab bar.
99
- super("pager", { flexShrink: 1, ...style }, tabs as any)
100
- this._lens = tabs.map(() => 1)
101
- for (const s of this.children) this._track(s)
102
- }
103
-
104
- /** Start offset of tab `i`'s segment in the flat `children`. */
105
- private _offset(i: number): number {
106
- let off = 0
107
- for (let t = 0; t < i; t++) off += this._lens[t]
108
- return off
109
- }
110
-
111
- get index(): number {
112
- return this._index
113
- }
114
- get stack(): UIScreen[] {
115
- const off = this._offset(this._index)
116
- return this.children.slice(off, off + (this._lens[this._index] ?? 0))
117
- }
118
- get depth(): number {
119
- return this._lens[this._index] ?? 0
120
- }
121
-
122
- onSelect(callback: (index: number) => void): this {
123
- this.scl.push(callback)
124
- return this
125
- }
126
- onChange(callback: (depth: number) => void): this {
127
- this.ncl.push(callback)
128
- return this
129
- }
130
-
131
- select(index: number, animated = false): this {
132
- if (index < 0 || index >= this._lens.length || index === this._index) return this
133
- this._index = index
134
- if (this._id !== 0) _creatorUI.command(this, "select", animated)
135
- this._emitSelect()
136
- return this
137
- }
138
-
139
- push(screen: UIScreen): this {
140
- if (this._lens.length === 0) {
141
- console.warn("UIPager.push: pager has no tabs")
142
- return this
143
- }
144
- this.children.splice(this._offset(this._index) + this._lens[this._index], 0, screen)
145
- this._lens[this._index]++
146
- this._track(screen)
147
- if (this._id !== 0) _creatorUI.command(this, "push")
148
- this._emitChange()
149
- return this
150
- }
151
-
152
- pop(): this {
153
- if ((this._lens[this._index] ?? 0) <= 1) return this
154
- this.children.splice(this._offset(this._index) + this._lens[this._index] - 1, 1)
155
- this._lens[this._index]--
156
- if (this._id !== 0) _creatorUI.command(this, "pop")
157
- this._emitChange()
158
- return this
159
- }
160
-
161
- popToRoot(): this {
162
- const len = this._lens[this._index] ?? 0
163
- if (len <= 1) return this
164
- this.children.splice(this._offset(this._index) + 1, len - 1)
165
- this._lens[this._index] = 1
166
- if (this._id !== 0) _creatorUI.command(this, "popToRoot")
167
- this._emitChange()
168
- return this
169
- }
170
-
171
- replace(screen: UIScreen): this {
172
- if (this._lens.length === 0) {
173
- console.warn("UIPager.replace: pager has no tabs")
174
- return this
175
- }
176
- this.children[this._offset(this._index) + this._lens[this._index] - 1] = screen
177
- this._track(screen)
178
- if (this._id !== 0) _creatorUI.command(this, "replace")
179
- this._emitChange()
180
- return this
181
- }
182
-
183
- // ── Children ARE the tabs ──────────────────────────────────────────────────────────────────
184
- // Every inherited container mutation would send a generic setContent/insertNode/removeNode wire
185
- // op, and a host must never run those on a pager: its children are page ROOTS the host owns
186
- // (each laid out independently at the pager's box), not yoga children — the generic path frees
187
- // the live pages and rebuilds them as ordinary children stacked on the first tab. So each one is
188
- // overridden to mutate the element first (children, `_lens`, `_index`) and then notify over the
189
- // same command channel every other pager op uses, where the host reconciles against the node.
190
- //
191
- // Content set this way is always one screen per tab: a per-tab STACK only ever grows through
192
- // push/replace, so `_lens` resets to all-ones here.
193
- private _rebuild(children: any[]): this {
194
- const tabs = children.filter(Boolean)
195
- const prevIndex = this._index
196
- this.children.length = 0
197
- this.children.push(...tabs)
198
- this._lens = tabs.map(() => 1)
199
- if (this._index >= tabs.length) this._index = Math.max(0, tabs.length - 1)
200
- for (const s of tabs) this._track(s)
201
- if (this._id !== 0) _creatorUI.command(this, "content")
202
- if (this._index !== prevIndex) this._emitSelect()
203
- this._emitChange()
204
- return this
205
- }
206
-
207
- setContent(children: UINodeChild[] | ChildrenFn): this {
208
- if (typeof children === "function") {
209
- this._bindChildren(children) // reactive children land in _reconcile below
210
- return this
211
- }
212
- disposeBinding(this, "#children") // static content overrides a reactive binding
213
- return this._rebuild(children as any[])
214
- }
215
- append(...nodes: UINodeChild[]): this {
216
- return this._rebuild([...this.children, ...nodes])
217
- }
218
- insert(index: number, ...nodes: UINodeChild[]): this {
219
- const next = this.children.slice()
220
- next.splice(index, 0, ...(nodes as any[]))
221
- return this._rebuild(next)
222
- }
223
- remove(...nodesToDelete: UIScreen[]): this {
224
- const drop = new Set<any>(nodesToDelete)
225
- return this._rebuild(this.children.filter((c: any) => !drop.has(c)))
226
- }
227
- _reconcile(next: UINodeChild[]): void {
228
- this._rebuild(next as any[])
229
- }
230
-
231
- // Host-driven tab change (committed swipe): the native pager already moved — sync the logical
232
- // index + fire onSelect. Named `_*` so chisel keeps it (hosts call it).
233
- _hostDidSelect(index: number): void {
234
- if (index < 0 || index >= this._lens.length || index === this._index) return
235
- this._index = index
236
- this._emitSelect()
237
- }
238
-
239
- // Host-driven pop on the current tab (interactive edge back-swipe): the native stack already
240
- // popped — sync the logical stack + fire onChange. Idempotent, safe after every transition.
241
- _hostDidPop(depth: number): void {
242
- if (depth < 1) depth = 1
243
- const len = this._lens[this._index] ?? 0
244
- if (depth >= len) return
245
- this.children.splice(this._offset(this._index) + depth, len - depth)
246
- this._lens[this._index] = depth
247
- this._emitChange()
248
- }
249
-
250
- private _emitSelect(): void {
251
- const i = this._index
252
- for (const cb of this.scl) cb(i)
253
- }
254
- private _emitChange(): void {
255
- const d = this._lens[this._index] ?? 0
256
- for (const cb of this.ncl) cb(d)
257
- }
258
-
259
- // Register a screen for `current` tracking: while a screen owned by this pager is the visible
260
- // top, this pager is active. The hook reads screen._pagerOwner LIVE (not a captured `this`), so
261
- // re-homing a screen into another pager activates the right one.
262
- private _track(screen: any): void {
263
- screen._pagerOwner = this
264
- if (!screen._pagerHooked) {
265
- screen._pagerHooked = true
266
- screen.onOpen(() => screen._pagerOwner?._activate())
267
- screen.onClose(() => screen._pagerOwner?._deactivate())
268
- }
269
- }
270
- _activate(): void {
271
- if (++this._openCount === 1 && _activeStack.indexOf(this) < 0) {
272
- _activeStack.push(this)
273
- }
274
- }
275
- _deactivate(): void {
276
- if (--this._openCount <= 0) {
277
- this._openCount = 0
278
- const i = _activeStack.indexOf(this)
279
- if (i >= 0) _activeStack.splice(i, 1)
280
- }
281
- }
282
- }
283
-
284
- const isScreenArg = (v: any): boolean => v != null && typeof v === "object" && v.type === "screen"
285
-
286
- /** Callable factory + ambient statics. The name `UIPager` is a value (this factory) and a type
287
- * (the instance interface above) — a standard value/type merge. */
288
- interface UIPagerConstructor {
289
- /** Tabs — each argument is a screen or an array of screens (arrays flatten one level, falsy
290
- * entries are skipped). One screen → a plain navigation stack rooted at it (push/pop, no tab
291
- * swiping); several → swipeable sibling tabs, fixed at construction, each starting as its own
292
- * one-screen stack. */
293
- (...tabs: (UIScreen | UIScreen[] | false | null | undefined)[]): UIPager
294
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
295
- (style: UIPagerStyle, content?: UIScreen | UIScreen[]): UIPager
296
- /** The pager that owns the currently visible screen, or `null` when the visible screen isn't
297
- * hosted by one. With pagers nested inside pages, resolves to the **innermost** — so ambient
298
- * calls act on the pager the user is actually looking at. */
299
- readonly current: UIPager | null
300
- /** `UIPager.current.push(screen)` — push onto the pager owning the visible screen, no
301
- * reference threading. No-op with a console warning when there is no current pager. */
302
- push(screen: UIScreen): void
303
- /** `UIPager.current.pop()` — pop the visible pager's current tab (no-op at the tab root, or
304
- * with a warning when there is no current pager). */
305
- pop(): void
306
- /** `UIPager.current.popToRoot()` — unwind the visible pager's current tab to its root. */
307
- popToRoot(): void
308
- }
309
-
310
- function pagerFactory(...args: any[]): UIPager {
311
- let style: any = null
312
- let start = 0
313
- const a0 = args.length > 0 ? args[0] : undefined
314
- if (a0 != null && typeof a0 === "object" && !Array.isArray(a0) && !isScreenArg(a0)) {
315
- style = a0
316
- start = 1
317
- }
318
- const tabs: any[] = []
319
- for (let i = start; i < args.length; i++) {
320
- const a = args[i]
321
- if (Array.isArray(a)) {
322
- for (let j = 0; j < a.length; j++) if (a[j]) tabs.push(a[j])
323
- } else if (a) {
324
- tabs.push(a)
325
- }
326
- }
327
- return new PagerElement(style, tabs) as unknown as UIPager
328
- }
329
-
330
- /**
331
- * Create a pager — THE screen-navigation element: an array of screens becomes swipeable sibling
332
- * tabs, a single screen becomes a plain stack. Navigate within it via `pager.push/pop/replace`
333
- * (per-tab) and `pager.select(i)` (between tabs), or from any child screen via the ambient
334
- * `UIPager.push` / `UIPager.pop` / `UIPager.current`. Pages are full `UIScreen`s (own
335
- * `onOpen`/`onClose`); give the pager `flexGrow: 1` or a size so it doesn't
336
- * collapse. `Router` stays the way to open a destination ON TOP of the pager.
337
- */
338
- export const UIPager = pagerFactory as unknown as UIPagerConstructor
339
-
340
- const _resolveCurrent = (): UIPager | null => {
341
- const cur = _activeStack[_activeStack.length - 1]
342
- if (!cur) {
343
- console.warn("UIPager.push/pop: no active pager on the current screen")
344
- return null
345
- }
346
- return cur as unknown as UIPager
347
- }
348
-
349
- // The ambient statics. These are top-level statements in a `sideEffects: false` module, so they only
350
- // survive bundling because chisel treats a statement that *mutates a binding this module declares*
351
- // as part of that binding's declaration (`ItemKind::AttachedTo`, chisel >= 0.4.2) — kept iff
352
- // `UIPager` is reached, shaken away with it otherwise. Under chisel <= 0.4.1 they were dropped
353
- // outright and `UIPager.push` arrived as `undefined` at runtime, in bundled builds only; don't lower
354
- // the pinned `@letary/chisel` back past 0.4.2. Covered by sdk/tests/compile.test.ts.
355
- // `Object.defineProperty` for `current`, not `Object.assign({ get current() {} })` — Object.assign
356
- // *invokes* the getter and copies the resulting value, freezing it at null forever.
357
- Object.defineProperty(UIPager, "current", {
358
- get: () => (_activeStack[_activeStack.length - 1] ?? null) as unknown as UIPager | null,
359
- })
360
- ;(UIPager as any).push = (screen: UIScreen) => { _resolveCurrent()?.push(screen) }
361
- ;(UIPager as any).pop = () => { _resolveCurrent()?.pop() }
362
- ;(UIPager as any).popToRoot = () => { _resolveCurrent()?.popToRoot() }
@@ -1,100 +0,0 @@
1
- import { ModalElement, type UIModal, type UIModalStyle } from "./UIModal"
2
- import { Presentable } from "./presentable"
3
- import { device } from "../runtime/device"
4
- import { buildUI, type BoundingClientRect, type ChildrenFn, type UIChildArg, type UINodeChild } from "./UINode"
5
-
6
- export type UIPopoverStyle = UIModalStyle
7
-
8
- /** What a popover can anchor to: any element (it reads `getBoundingClientRect()`), or a raw
9
- * `{x, y}` point — a long-press context menu anchors to the touch position. */
10
- export type UIPopoverAnchor = { getBoundingClientRect(): BoundingClientRect | null } | { x: number, y: number }
11
-
12
- const POPOVER_ANIM_MS = 120
13
- const ANCHOR_GAP = 4 // between the anchor's edge and the popover
14
- const VIEWPORT_MARGIN = 8 // minimum distance kept to every screen edge
15
-
16
- /**
17
- * An anchored menu: a `UIModal` whose scrim is transparent (invisible but still intercepting —
18
- * an outside tap dismisses, and nothing underneath can scroll while it's open) and whose
19
- * position comes from the anchor passed to `show(anchor)`: below the anchor's left edge,
20
- * flipped above when there's no room, clamped into the viewport. Position is computed once per
21
- * show — anchored popovers don't follow their anchor. Backs dropdowns, context menus, and
22
- * tooltips; create it **once** at module scope and reuse it.
23
- */
24
- export interface UIPopover extends UIModal {
25
- /** Position next to `anchor` (an element or an `{x, y}` point), attach to
26
- * `Presentable.current` (unless an owner was set via `attachTo`), and play the entrance
27
- * transition. Without an anchor the popover shows wherever its own style puts it. */
28
- show(anchor?: UIPopoverAnchor): void,
29
- }
30
-
31
- export class PopoverElement extends ModalElement {
32
- private _anchorRect?: { left: number, top: number, right: number, bottom: number }
33
- private _placedLeft?: number
34
- private _placedTop?: number
35
-
36
- constructor(style: UIPopoverStyle, children: UINodeChild[] | ChildrenFn) {
37
- super({ overlayColor: "transparent", ...style }, children)
38
- this.transition({ opacity: 0, duration: POPOVER_ANIM_MS })
39
- // The flip/clamp decision needs the popover's measured size, which exists only after the
40
- // mount lays it out — so placement is corrected on layout, inside the entrance fade.
41
- this.onLayout(({ width, height }) => this._place(width, height))
42
- }
43
-
44
- show(anchor?: UIPopoverAnchor): void {
45
- if (anchor) {
46
- const r = "getBoundingClientRect" in anchor
47
- ? anchor.getBoundingClientRect()
48
- : { left: anchor.x, top: anchor.y, right: anchor.x, bottom: anchor.y }
49
- if (r) {
50
- this._anchorRect = { left: r.left, top: r.top, right: r.right, bottom: r.bottom }
51
- this._placedLeft = undefined
52
- this._placedTop = undefined
53
- // Preliminary spot (below, left-aligned) so the first paint is already close; _place
54
- // refines it once the size is known.
55
- this.style({ left: r.left, top: r.bottom + ANCHOR_GAP })
56
- }
57
- } else {
58
- this._anchorRect = undefined // unanchored show: the user's own style owns the position
59
- }
60
- // A menu belongs to the page it opened on: it hides with it and rides its transition. An
61
- // explicit attachTo() owner (e.g. a scene HUD's popover) is respected.
62
- if (!this._owner) this.attachTo(Presentable.current)
63
- super.show()
64
- }
65
-
66
- private _place(width: number, height: number): void {
67
- const anchor = this._anchorRect
68
- if (!anchor || !this._open) return
69
- const displayWidth = device.width, displayHeight = device.height // 0 on hosts without a size
70
- let left = anchor.left
71
- let top = anchor.bottom + ANCHOR_GAP
72
- if (displayWidth > 0) {
73
- left = Math.min(left, displayWidth - width - VIEWPORT_MARGIN)
74
- left = Math.max(left, VIEWPORT_MARGIN)
75
- }
76
- if (displayHeight > 0 && top + height > displayHeight - VIEWPORT_MARGIN) {
77
- const above = anchor.top - ANCHOR_GAP - height
78
- top = above >= VIEWPORT_MARGIN ? above : Math.max(VIEWPORT_MARGIN, displayHeight - VIEWPORT_MARGIN - height)
79
- }
80
- // Idempotence guard: hosts re-fire onLayout on recalc, and writing the same position would
81
- // trigger another recalc — only write when the target actually moved.
82
- if (this._placedLeft !== undefined && Math.abs(left - this._placedLeft) < 0.5
83
- && this._placedTop !== undefined && Math.abs(top - this._placedTop) < 0.5) return
84
- this._placedLeft = left
85
- this._placedTop = top
86
- this.style({ left, top })
87
- }
88
- }
89
-
90
- const makePopover = (style: any, children: any) => new PopoverElement(style, children)
91
-
92
- /** Create an anchored popover (see {@link UIPopover}) — create once at module scope, then
93
- * `show(anchor)` next to an element or `{x, y}` point. Same argument forms as `UIColumn`. */
94
- export function UIPopover(...children: UIChildArg[]): UIPopover;
95
- export function UIPopover(children: UINodeChild[] | ChildrenFn): UIPopover;
96
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
97
- export function UIPopover(style: UIPopoverStyle, children?: UINodeChild[] | ChildrenFn): UIPopover;
98
- export function UIPopover(...args: any[]): UIPopover {
99
- return buildUI(args, null, makePopover)
100
- }
@@ -1,123 +0,0 @@
1
- import { TouchStartEvent } from "../runtime/touch"
2
- import { _bumpNavEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "./presentable"
3
- import { ContainerElement, buildUI, type BaseStyle, type ContainerStyle, type DrawableStyle, type PaddingStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, } from "./UINode"
4
-
5
- export type UIScreenStyle = ContainerStyle & DrawableStyle & PaddingStyle & BaseStyle
6
-
7
- /** A screen — the root the router/host mounts, sized to its slot. A screen NEVER scrolls: it is
8
- * fixed chrome (header, tab bar) plus, when the content overflows, one `UIScrollable` body child
9
- * (`flexGrow: 1`). Scroll events and pull-to-refresh live on the scroll containers
10
- * (`UIScrollable`, `UIVirtualizedList`). */
11
- export interface UIScreen extends UIContainerBase<UIScreenStyle, DrawableStyle & BaseStyle> {
12
- readonly type: "screen",
13
-
14
- /** Show this screen as the current destination (replaces whatever is visible — a screen, a
15
- * scene, a native view — suspending an active Router until `Router.restore()`). */
16
- open(options?: PresentOptions): void,
17
- /** Dismiss if this screen is the visible destination. */
18
- close(): void
19
-
20
- /** Hardware/system back while this screen is current. */
21
- onBackPressed(callback: () => void): this
22
-
23
- /** The screen became the visible destination (first open, or revealed by a pop). */
24
- onOpen(callback: () => void): this
25
- /** The screen stopped being visible — closed, replaced, or covered by a push. */
26
- onClose(callback: () => void): this
27
-
28
- /**
29
- * Keep this screen's built native tree alive when a host detaches it: instead of being destroyed,
30
- * its views + layout + live state (scroll position, input text) are retained, so re-hosting the
31
- * SAME screen object restores it in place rather than rebuilding. `onClose`/`onOpen` still fire on
32
- * detach/re-attach. You own the lifetime — a kept tree is freed only by `dispose()` or when the
33
- * project unloads. (Native-only today; web rebuilds.)
34
- */
35
- keepAlive(enabled?: boolean): this
36
- /** Free a `keepAlive()` screen's retained tree now. No-op unless it's currently detached-and-kept. */
37
- dispose(): this
38
-
39
- /** Touch began on the screen; `ev.track(...)` takes over the rest of the gesture. */
40
- onTouchStart(callback: (ev: TouchStartEvent<UIScreen>) => void): this,
41
- }
42
-
43
- export class ScreenElement extends ContainerElement<"screen"> {
44
- open(options?: PresentOptions): void {
45
- _bumpNavEpoch()
46
- if (_navSupported()) {
47
- _creatorUI.openView!(this, options?.transition ?? "none")
48
- } else {
49
- // Legacy host: no destination navigation — transition is dropped.
50
- _creatorUI.openScreen(this)
51
- }
52
- _setCurrent(this as any)
53
- }
54
- close(): void {
55
- _bumpNavEpoch()
56
- if (_navSupported()) {
57
- _creatorUI.closeView!()
58
- } else {
59
- _creatorUI.closeScreen()
60
- }
61
- if (Presentable.current === (this as any)) _setCurrent(null)
62
- }
63
- protected ol: (() => void)[] = []
64
- onOpen(callback: () => void): this {
65
- this.ol.push(callback)
66
- return this
67
- }
68
- protected cl: (() => void)[] = []
69
- onClose(callback: () => void): this {
70
- this.cl.push(callback)
71
- return this
72
- }
73
- // Read by the host at detach time (a plain flag on the node, like _interactive).
74
- _keepAlive = false
75
- keepAlive(enabled = true): this {
76
- this._keepAlive = enabled
77
- return this
78
- }
79
- dispose(): this {
80
- // Rides the generic command channel (no dedicated bridge). No-op while _id is 0 (never mounted)
81
- // or when the tree isn't currently detached-and-kept — the host ignores it then.
82
- if (this._id !== 0) _creatorUI.command(this, "disposeKeepAlive")
83
- return this
84
- }
85
- readonly touchStartListeners: any[] = []
86
- _interactive = false
87
- onTouchStart (callback: any): this {
88
- this._interactive = true
89
- this.touchStartListeners.push(callback)
90
- return this
91
- }
92
- _emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
93
- const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
94
- touchEvent.target = this
95
- for (let callback of this.touchStartListeners) {
96
- callback(touchEvent)
97
- if ((touchEvent as any)._trackHandler) {
98
- return (touchEvent as any)._trackHandler
99
- }
100
- }
101
- return null
102
- }
103
- _backButtonCallback?: () => void
104
- onBackPressed(callback: any): this {
105
- this._backButtonCallback = callback
106
- return this
107
- }
108
- }
109
-
110
- const makeScreen = (style: any, children: any) => new ScreenElement("screen", style, children)
111
-
112
- /** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
113
- export const __UIScreen = (children: UINodeChild[] | ChildrenFn): UIScreen => makeScreen({}, children)
114
-
115
- /** Create a screen. Present it with `.open()`, or via `Router` / `UIPager`. Same argument forms
116
- * as `UIColumn`. */
117
- export function UIScreen(...children: UIChildArg[]): UIScreen;
118
- export function UIScreen(children: UINodeChild[] | ChildrenFn): UIScreen;
119
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
120
- export function UIScreen(style: UIScreenStyle, children?: UINodeChild[] | ChildrenFn): UIScreen;
121
- export function UIScreen(...args: any[]): UIScreen {
122
- return buildUI(args, null, makeScreen)
123
- }
@@ -1,87 +0,0 @@
1
- import type { UIContainerStyle } from "./UIContainer"
2
- import { type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type Color, ContainerElement, buildUI } from "./UINode"
3
-
4
- export type ScrollStyle = {
5
- showScrollbar?: boolean,
6
- overscrollMode?: "none" | "absorb" | "default",
7
- /** Tint of the native pull-to-refresh spinner (pairs with `onRefresh`). */
8
- refreshControlColor?: Color,
9
- /** How a scroll gesture in THIS scrollable dismisses the keyboard: `"interactive"` (default) —
10
- * dragging over the keyboard slides it away (chat feel); `"scroll"` — any drag dismisses
11
- * immediately; `"none"` — never (tap-outside policy still applies). */
12
- keyboardDismissMode?: "interactive" | "scroll" | "none",
13
- /** Paging: when the drag ends, rest on a direct child's boundary along the scroll axis — the
14
- * value picks where the child sits in the viewport; `"none"` (default) is free scrolling. Snap
15
- * targets are the children themselves, so item widths can differ (card-carousel-with-peek); a
16
- * carousel is a horizontal scrollable of full-width children + `snap: "start"`. */
17
- snap?: "none" | "start" | "center" | "end"
18
- }
19
-
20
- export type UIScrollableStyle = UIContainerStyle & ScrollStyle & { scrollDirection?: "horizontal" | "vertical" }
21
-
22
- /** A scroll container — vertical by default, `scrollDirection: "horizontal"` for carousels/chip
23
- * rows. The scrolling body of a screen (screens themselves never scroll). */
24
- export interface UIScrollable extends UIContainerBase<UIScrollableStyle> {
25
- readonly type: "scrollable",
26
-
27
- /** Scroll position changed (px from the start edge). */
28
- onScroll(callback: (scrollPosition: number) => void): UIScrollable,
29
- /** The scroll gesture ended (finger lifted). */
30
- onScrollRelease(callback: () => void): UIScrollable,
31
- /** Pulled past the content edge by `delta` px. */
32
- onOverscroll(callback: (delta: number) => void): UIScrollable
33
- /** Pull-to-refresh: the spinner stays until the returned promise settles. Attach BEFORE the
34
- * node mounts (hosts read it at creation). Vertical only; native hosts — no-op on web. */
35
- onRefresh(callback: () => void | Promise<void>): UIScrollable
36
- }
37
-
38
- class ScrollableElement extends ContainerElement<"scrollable"> {
39
-
40
- readonly osl: any[] = []
41
- readonly sl: any[] = []
42
- readonly srl: any[] = []
43
-
44
- get scrollListeners() {
45
- return this.sl
46
- }
47
-
48
- onScroll (callback: any): this {
49
- this.sl.push(callback)
50
- return this
51
- }
52
-
53
- onScrollRelease (callback: any): this {
54
- this.srl.push(callback)
55
- return this
56
- }
57
-
58
- onOverscroll (callback: any): this {
59
- this.osl.push(callback)
60
- return this
61
- }
62
-
63
- // Read by the host at mount (same contract the scrollable screen had before 2026-07-22).
64
- declare _refreshCallback: any
65
- onRefresh (callback: any): this {
66
- this._refreshCallback = callback
67
- return this
68
- }
69
- }
70
-
71
- // flexShrink: 1 default (SDK-owned, like UIRow's flexDirection): without it a scrollable in a
72
- // flex parent grows to its content height and overflows instead of scrolling.
73
- const SCROLLABLE_DEFAULTS = { flexShrink: 1 } as const
74
- const makeScrollable = (style: any, children: any) => new ScrollableElement("scrollable", style, children)
75
-
76
- /** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
77
- export const __UIScrollable = (children: UINodeChild[] | ChildrenFn): UIScrollable => makeScrollable({ ...SCROLLABLE_DEFAULTS }, children)
78
-
79
- /** Create a scroll container (same argument forms as `UIColumn`). As a screen body, pair with
80
- * `flexGrow: 1`. */
81
- export function UIScrollable(...children: UIChildArg[]): UIScrollable
82
- export function UIScrollable(children: UINodeChild[] | ChildrenFn): UIScrollable
83
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
84
- export function UIScrollable(style: UIScrollableStyle, children?: UINodeChild[] | ChildrenFn): UIScrollable
85
- export function UIScrollable(...args: any[]): UIScrollable {
86
- return buildUI(args, SCROLLABLE_DEFAULTS, makeScrollable)
87
- }