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,148 +1,154 @@
1
- // Editor gizmos: immediate-mode line drawing for code that runs while a scene is EDITED — a
2
- // generator aspect's `rebuild()`, a `make()` factory, an editor tool's hooks. Nothing here touches
3
- // the scene: calls accumulate WORLD-space line segments into the buffer of the run that is
4
- // currently executing (an ambient scope the scene loader opens around each synchronous call), and
5
- // the scene editor pushes those buffers to the lite engine's overlay layer, which draws them over
6
- // the picture — never outlined, never picked, never rendered by Filament, absent in play mode.
7
- //
8
- // rebuild() {
9
- // Gizmos.polyline(points, { color: "#5b8ef0", closed: true })
10
- // for (const p of points) Gizmos.cross(p, 0.07)
11
- // }
12
- //
13
- // Every run starts with an EMPTY buffer — what a call draws is the whole picture (like
14
- // `generated.clear()`, only implicit). Outside any scope (play mode, hand-attached aspects, a call
15
- // after an `await` inside rebuild) the calls are no-ops: rebuild is synchronous by contract.
16
- //
17
- // ANCHORED gizmos (`{ node }` in the style): points are in that node's LOCAL frame and the engine
18
- // follows the node live — through a gizmo drag, not only after it — with the node's scale
19
- // stripped (an empty scaled ×10 for its children keeps a normal-sized cross). An anchored picture
20
- // is the node's own: clicking it in the viewport selects the node, and it draws in the selection
21
- // accent while selected. World-space calls (path lines) are never pickable.
22
-
23
- import { Mat4, type Mat4Like } from "../math/mat4"
24
- import type { Vec3Like } from "../math/vec"
25
-
26
- /** The anchor of a gizmo call: any object with an engine entity id (a `Node`). */
27
- export type GizmoAnchor = { readonly id: number }
28
-
29
- export type GizmoStyle = {
30
- /** CSS hex color (`#rgb` / `#rrggbb`); default a neutral light gray. */
31
- color?: string
32
- /** 0..1, default 1. */
33
- alpha?: number
34
- /** Anchor: points are in this node's LOCAL frame (scale ignored); the lines follow the node
35
- * live, are pickable (a click selects the node) and turn the selection accent when it is
36
- * selected. Omit for world-space lines (not pickable). */
37
- node?: GizmoAnchor | null
38
- }
39
-
40
- /** One color batch of LINES (flat `[x,y,z, x,y,z]` per segment) — what a host hands the engine.
41
- * With `entityId` the segments are in that entity's local frame (see GizmoStyle.node). */
42
- export type GizmoBatch = { color: string, alpha: number, segments: number[], entityId?: number }
43
-
44
- const DEFAULT_COLOR = "#cfd4dd"
45
-
46
- /** The per-run collector (owned by the scene loader's EditorRun). Batches are keyed by style so a
47
- * hundred same-colored segments cost one draw. */
48
- export class GizmoBuffer {
49
- batches: GizmoBatch[] = []
50
- private _byStyle = new Map<string, GizmoBatch>()
51
-
52
- clear(): void {
53
- this.batches = []
54
- this._byStyle.clear()
55
- }
56
-
57
- segments(style: GizmoStyle | undefined): number[] {
58
- const color = style?.color ?? DEFAULT_COLOR
59
- const alpha = Math.max(0, Math.min(1, style?.alpha ?? 1))
60
- const entityId = style?.node?.id
61
- const key = `${color}@${alpha}@${entityId ?? ""}`
62
- let b = this._byStyle.get(key)
63
- if (!b) {
64
- b = entityId ? { color, alpha, segments: [], entityId } : { color, alpha, segments: [] }
65
- this._byStyle.set(key, b)
66
- this.batches.push(b)
67
- }
68
- return b.segments
69
- }
70
- }
71
-
72
- let current: GizmoBuffer | null = null
73
- let warnedOutOfScope = false
74
-
75
- const xyz = (p: Vec3Like): [number, number, number] =>
76
- Array.isArray(p) ? [ p[0] ?? 0, p[1] ?? 0, p[2] ?? 0 ] : [ (p as { x: number }).x, (p as { y: number }).y, (p as { z: number }).z ]
77
-
78
- const target = (style: GizmoStyle | undefined): number[] | null => {
79
- if (current) return current.segments(style)
80
- if (!warnedOutOfScope && (globalThis as { __lecodesSceneEdit?: boolean }).__lecodesSceneEdit) {
81
- warnedOutOfScope = true
82
- console.warn("[Gizmos] draw call outside an editor run — gizmos must be drawn synchronously inside rebuild() / a make() factory / a tool hook")
83
- }
84
- return null
85
- }
86
-
87
- /** @internal Run `fn` with `buffer` as the ambient gizmo target (cleared first). Nested scopes
88
- * restore the outer one. */
89
- export const withGizmoScope = <T>(buffer: GizmoBuffer, fn: () => T): T => {
90
- const prev = current
91
- buffer.clear()
92
- current = buffer
93
- try { return fn() } finally { current = prev }
94
- }
95
-
96
- /**
97
- * Editor-only line drawing, available inside generator `rebuild()`, `make()` factories and editor
98
- * tool hooks. Points are WORLD space. Drawn by the scene editor's viewport overlay (depth-test
99
- * off — a path through a wall is still a path); invisible everywhere else.
100
- */
101
- export const Gizmos = {
102
- /** One segment from `a` to `b`. */
103
- line(a: Vec3Like, b: Vec3Like, style?: GizmoStyle): void {
104
- const out = target(style)
105
- if (!out) return
106
- out.push(...xyz(a), ...xyz(b))
107
- },
108
-
109
- /** Consecutive segments through `points`; `closed` joins the last point back to the first. */
110
- polyline(points: readonly Vec3Like[], style?: GizmoStyle & { closed?: boolean }): void {
111
- if (points.length < 2) return
112
- const out = target(style)
113
- if (!out) return
114
- const n = points.length
115
- const last = style?.closed ? n : n - 1
116
- for (let i = 0; i < last; i++) out.push(...xyz(points[i]!), ...xyz(points[(i + 1) % n]!))
117
- },
118
-
119
- /** A wireframe camera frustum looking down −Z: the four edges from the origin to a rect
120
- * `length` metres ahead sized by `fov` (vertical, degrees) × `aspect` (default 16:9), plus an
121
- * "up" fin above the rect. Anchor it (`{ node }`) for a camera node's marker — the frustum
122
- * then follows the node's pose; `matrix` places an unanchored one in world space. */
123
- frustum(fov: number, style?: GizmoStyle & { aspect?: number, length?: number, matrix?: Mat4Like }): void {
124
- const out = target(style)
125
- if (!out) return
126
- const aspect = style?.aspect ?? 16 / 9, length = style?.length ?? 0.6
127
- const m = style?.matrix ? new Mat4(style.matrix) : null
128
- const h = Math.tan((fov * Math.PI) / 360) * length, w = h * aspect, z = -length
129
- const P = (x: number, y: number, zz: number): [number, number, number] => {
130
- if (!m) return [ x, y, zz ]
131
- const v = m.transformPoint([ x, y, zz ])
132
- return [ v.x, v.y, v.z ]
133
- }
134
- const o = P(0, 0, 0)
135
- const c = [ P(-w, -h, z), P(w, -h, z), P(w, h, z), P(-w, h, z) ]
136
- for (const q of c) out.push(...o, ...q)
137
- for (let i = 0; i < 4; i++) out.push(...c[i]!, ...c[(i + 1) % 4]!)
138
- out.push(...P(-w * 0.4, h, z), ...P(0, h * 1.6, z), ...P(0, h * 1.6, z), ...P(w * 0.4, h, z))
139
- },
140
-
141
- /** A three-axis cross centred on `p` (a point marker); `size` = half extent, default 0.1. */
142
- cross(p: Vec3Like, size = 0.1, style?: GizmoStyle): void {
143
- const out = target(style)
144
- if (!out) return
145
- const [ x, y, z ] = xyz(p)
146
- out.push(x - size, y, z, x + size, y, z, x, y - size, z, x, y + size, z, x, y, z - size, x, y, z + size)
147
- },
148
- }
1
+ // Editor gizmos: immediate-mode line drawing for code that runs while a scene is EDITED — a
2
+ // generator aspect's `rebuild()`, a `make()` factory, an editor tool's hooks. Nothing here touches
3
+ // the scene: calls accumulate WORLD-space line segments into the buffer of the run that is
4
+ // currently executing (an ambient scope the scene loader opens around each synchronous call), and
5
+ // the scene editor pushes those buffers to the lite engine's overlay layer, which draws them over
6
+ // the picture — never outlined, never picked, never rendered by Filament, absent in play mode.
7
+ //
8
+ // rebuild() {
9
+ // Gizmos.polyline(points, { color: "#5b8ef0", closed: true })
10
+ // for (const p of points) Gizmos.cross(p, 0.07)
11
+ // }
12
+ //
13
+ // Every run starts with an EMPTY buffer — what a call draws is the whole picture (like
14
+ // `generated.clear()`, only implicit). Outside any scope (play mode, hand-attached aspects, a call
15
+ // after an `await` inside rebuild) the calls are no-ops: rebuild is synchronous by contract. In a
16
+ // shipped bundle they are no-ops by COMPILE: `target` folds to null with `EDITOR`, and the buffer
17
+ // and the scope (the editor's — ./editor.ts) are not in the bundle.
18
+ //
19
+ // ANCHORED gizmos (`{ node }` in the style): points are in that node's LOCAL frame and the engine
20
+ // follows the node live — through a gizmo drag, not only after it — with the node's scale
21
+ // stripped (an empty scaled ×10 for its children keeps a normal-sized cross). An anchored picture
22
+ // is the node's own: clicking it in the viewport selects the node, and it draws in the selection
23
+ // accent while selected. World-space calls (path lines) are never pickable.
24
+
25
+ import { Color, type ColorInput } from "../core/color"
26
+ import { Mat4, type Mat4Like } from "../math/mat4"
27
+ import type { Vec3Like } from "../math/vec"
28
+
29
+ /** The anchor of a gizmo call: any object with an engine entity id (a `Node`). */
30
+ export type GizmoAnchor = { readonly id: number }
31
+
32
+ export type GizmoStyle = {
33
+ /** Any color (a CSS color string, `0xRRGGBB`, `[r, g, b(, a)]`; its alpha multiplies `alpha`);
34
+ * default a neutral light gray. */
35
+ color?: ColorInput
36
+ /** 0..1, default 1. */
37
+ alpha?: number
38
+ /** Anchor: points are in this node's LOCAL frame (scale ignored); the lines follow the node
39
+ * live, are pickable (a click selects the node) and turn the selection accent when it is
40
+ * selected. Omit for world-space lines (not pickable). */
41
+ node?: GizmoAnchor | null
42
+ }
43
+
44
+ /** One color batch of LINES (flat `[x,y,z, x,y,z]` per segment) — what a host hands the engine
45
+ * (`color` a CSS color string, '#rrggbbaa' from here). With `entityId` the segments are in that
46
+ * entity's local frame (see GizmoStyle.node). */
47
+ export type GizmoBatch = { color: string, alpha: number, segments: number[], entityId?: number }
48
+
49
+ const DEFAULT_COLOR = "#cfd4ddff"
50
+
51
+ /** The per-run collector (owned by the scene loader's EditorRun). Batches are keyed by style so a
52
+ * hundred same-colored segments cost one draw. */
53
+ export class GizmoBuffer {
54
+ batches: GizmoBatch[] = []
55
+ private _byStyle = new Map<string, GizmoBatch>()
56
+
57
+ clear(): void {
58
+ this.batches = []
59
+ this._byStyle.clear()
60
+ }
61
+
62
+ segments(style: GizmoStyle | undefined): number[] {
63
+ // one spelling per color, so 'red' and '#f00' share a batch
64
+ const color = style?.color != null ? Color.toHexString(style.color) : DEFAULT_COLOR
65
+ const alpha = Math.max(0, Math.min(1, style?.alpha ?? 1))
66
+ const entityId = style?.node?.id
67
+ const key = `${color}@${alpha}@${entityId ?? ""}`
68
+ let b = this._byStyle.get(key)
69
+ if (!b) {
70
+ b = entityId ? { color, alpha, segments: [], entityId } : { color, alpha, segments: [] }
71
+ this._byStyle.set(key, b)
72
+ this.batches.push(b)
73
+ }
74
+ return b.segments
75
+ }
76
+ }
77
+
78
+ let current: GizmoBuffer | null = null
79
+ let warnedOutOfScope = false
80
+
81
+ const xyz = (p: Vec3Like): [number, number, number] =>
82
+ Array.isArray(p) ? [ p[0] ?? 0, p[1] ?? 0, p[2] ?? 0 ] : [ (p as { x: number }).x, (p as { y: number }).y, (p as { z: number }).z ]
83
+
84
+ const target = (style: GizmoStyle | undefined): number[] | null => {
85
+ if (EDITOR && current) return current.segments(style)
86
+ if (EDITOR && !warnedOutOfScope && (globalThis as { __lecodesSceneEdit?: boolean }).__lecodesSceneEdit) {
87
+ warnedOutOfScope = true
88
+ console.warn("[Gizmos] draw call outside an editor run — gizmos must be drawn synchronously inside rebuild() / a make() factory / a tool hook")
89
+ }
90
+ return null
91
+ }
92
+
93
+ /** @internal Run `fn` with `buffer` as the ambient gizmo target (cleared first). Nested scopes
94
+ * restore the outer one. */
95
+ export const withGizmoScope = <T>(buffer: GizmoBuffer, fn: () => T): T => {
96
+ const prev = current
97
+ buffer.clear()
98
+ current = buffer
99
+ try { return fn() } finally { current = prev }
100
+ }
101
+
102
+ /**
103
+ * Editor-only line drawing, available inside generator `rebuild()`, `make()` factories and editor
104
+ * tool hooks. Points are WORLD space. Drawn by the scene editor's viewport overlay (depth-test
105
+ * off — a path through a wall is still a path); invisible everywhere else.
106
+ */
107
+ export const Gizmos = {
108
+ /** One segment from `a` to `b`. */
109
+ line(a: Vec3Like, b: Vec3Like, style?: GizmoStyle): void {
110
+ const out = target(style)
111
+ if (!out) return
112
+ out.push(...xyz(a), ...xyz(b))
113
+ },
114
+
115
+ /** Consecutive segments through `points`; `closed` joins the last point back to the first. */
116
+ polyline(points: readonly Vec3Like[], style?: GizmoStyle & { closed?: boolean }): void {
117
+ if (points.length < 2) return
118
+ const out = target(style)
119
+ if (!out) return
120
+ const n = points.length
121
+ const last = style?.closed ? n : n - 1
122
+ for (let i = 0; i < last; i++) out.push(...xyz(points[i]!), ...xyz(points[(i + 1) % n]!))
123
+ },
124
+
125
+ /** A wireframe camera frustum looking down −Z: the four edges from the origin to a rect
126
+ * `length` metres ahead sized by `fov` (vertical, degrees) × `aspect` (default 16:9), plus an
127
+ * "up" fin above the rect. Anchor it (`{ node }`) for a camera node's marker — the frustum
128
+ * then follows the node's pose; `matrix` places an unanchored one in world space. */
129
+ frustum(fov: number, style?: GizmoStyle & { aspect?: number, length?: number, matrix?: Mat4Like }): void {
130
+ const out = target(style)
131
+ if (!out) return
132
+ const aspect = style?.aspect ?? 16 / 9, length = style?.length ?? 0.6
133
+ const m = style?.matrix ? new Mat4(style.matrix) : null
134
+ const h = Math.tan((fov * Math.PI) / 360) * length, w = h * aspect, z = -length
135
+ const P = (x: number, y: number, zz: number): [number, number, number] => {
136
+ if (!m) return [ x, y, zz ]
137
+ const v = m.transformPoint([ x, y, zz ])
138
+ return [ v.x, v.y, v.z ]
139
+ }
140
+ const o = P(0, 0, 0)
141
+ const c = [ P(-w, -h, z), P(w, -h, z), P(w, h, z), P(-w, h, z) ]
142
+ for (const q of c) out.push(...o, ...q)
143
+ for (let i = 0; i < 4; i++) out.push(...c[i]!, ...c[(i + 1) % 4]!)
144
+ out.push(...P(-w * 0.4, h, z), ...P(0, h * 1.6, z), ...P(0, h * 1.6, z), ...P(w * 0.4, h, z))
145
+ },
146
+
147
+ /** A three-axis cross centred on `p` (a point marker); `size` = half extent, default 0.1. */
148
+ cross(p: Vec3Like, size = 0.1, style?: GizmoStyle): void {
149
+ const out = target(style)
150
+ if (!out) return
151
+ const [ x, y, z ] = xyz(p)
152
+ out.push(x - size, y, z, x + size, y, z, x, y - size, z, x, y + size, z, x, y, z - size, x, y, z + size)
153
+ },
154
+ }
@@ -7,7 +7,7 @@
7
7
  // per-model type bags and MODELS metadata this file is parametrised by.
8
8
  //
9
9
  // Keep it dependency-free: besides shipping in `marcidb-client/runtime`, this file is vendored verbatim
10
- // into the lecodes SDK (`packages/sdk/src/server/db/marci/query.ts`, `bun run sync:marcidb` there), which
10
+ // into the lecodes SDK (`sdk/src/server/db/marci/query.ts`, `bun run sync:marcidb` there), which
11
11
  // derives its model types from TS builders instead of codegen and plugs them into the same generics.
12
12
 
13
13
  // ───────────────────────────── query language types ─────────────────────────────
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * `sdk/server` — the HOST side of the app backend, imported normally by the runner
3
- * (packages/lecodes-runner), the backend and tests. Nothing here is bundled into project code; the
3
+ * (tools/runner), the backend and tests. Nothing here is bundled into project code; the
4
4
  * bundle carries its own SDK copy and meets this module only through the `globalThis.__lecodes*`
5
5
  * seams (see ./runtime.ts).
6
6
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Host side of a compiled server bundle — used by the runner (packages/lecodes-runner) and by tests.
2
+ * Host side of a compiled server bundle — used by the runner (tools/runner) and by tests.
3
3
  * Imported normally (Node/Bun), NOT through chisel: the bundle carries its own SDK copy and talks
4
4
  * to this module only through globals (`__lecodesServer`, `__lecodesRequest`, `__lecodesPublish`).
5
5
  *
@@ -10,8 +10,10 @@
10
10
  // wrapper convention.
11
11
 
12
12
  import { _channelCall, _channelEmit, _channelOff, _channelOn, type ChannelListeners } from "../runtime/channel"
13
- import { _bumpNavEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "./presentable"
13
+ import { _bumpNavEpoch, _setCurrent, Presentable, type DestTuple, type DismissOptions, type PresentOptions } from "./presentable"
14
+ import { _transitionId, TRANSITION_NONE } from "./transitions"
14
15
  import { Element, type BaseStyle, type DrawableStyle, type ElementStyle, type UIElementBase } from "./UINode"
16
+ import { TREE_DEST_NATIVE, registerDest, registerNativeView, tree, unregisterNativeView } from "./tree"
15
17
 
16
18
  export type UINativeViewStyle = ElementStyle & DrawableStyle
17
19
 
@@ -28,25 +30,30 @@ export interface NativeView extends UIElementBase<UINativeViewStyle, DrawableSty
28
30
  /** Show fullscreen as the current destination (see Presentable). */
29
31
  open(options?: PresentOptions): void,
30
32
  /** Dismiss if this is the visible destination. */
31
- close(): void,
33
+ close(options?: DismissOptions): void,
32
34
 
33
35
  /** Became the visible destination. */
34
36
  onOpen(callback: () => void): this
35
37
  /** Stopped being the visible destination. */
36
38
  onClose(callback: () => void): this
37
39
  /** Hardware/system back while current. */
38
- onBackPressed(callback: () => void): this
40
+ onBack(callback: () => void): this
39
41
 
40
42
  /** Invoke a method on the native view (`map.call("setCenter", [lng, lat])`). Args and the
41
- * result are JSON-serialized. Rejects if the host has no such view/method. */
43
+ * result are plain data (and Uint8Array). Rejects if the host has no such view/method. */
42
44
  call(method: string, ...args: any[]): Promise<any>
43
45
  /** Subscribe to an event the native view emits (`map.on("markerTap", cb)`). */
44
46
  on(event: string, callback: (data?: any) => void): this
45
47
  off(event: string, callback: (data?: any) => void): this
48
+ /** @internal The destination tuple (Presentable). */
49
+ _dest(): DestTuple
46
50
  }
47
51
 
48
52
  let nextViewId = 1
49
53
 
54
+ const PRESENTED = 1
55
+ const MOUNTED = 2
56
+
50
57
  /** @internal Base for typed plugin wrappers (QRScanner, CameraView — src/plugins/): subclass it,
51
58
  * pin the viewName/params, and layer typed methods over the raw `call`/`on` channel. Not a
52
59
  * public global — apps use the `NativeView(name)` factory below. */
@@ -55,37 +62,41 @@ export class NativeViewElement extends Element<"native"> implements Presentable
55
62
  readonly params: any
56
63
  /** @internal stable per-instance handle for the viewCall channel + promotion identity. */
57
64
  readonly _viewId: number
58
- /** @internal event listeners by event name — the host invokes these (vlist pcl/psl convention)
59
- * or calls _emitViewEvent. */
65
+ /** @internal event listeners by event name (host → view events come through the tree emitter). */
60
66
  readonly nvl: ChannelListeners = {}
61
67
  readonly ol: (() => void)[] = []
62
68
  readonly cl: (() => void)[] = []
63
- _backButtonCallback?: () => void
64
- private _vd?: object
69
+ private _backButtonCallback?: () => void
70
+ private readonly _paramsJson: string
71
+ /** @internal Where the host has a view of this instance: PRESENTED | MOUNTED. */
72
+ private _shown = 0
65
73
 
66
74
  constructor(viewName: string, params: any) {
67
75
  super("native", {})
68
76
  this.viewName = viewName
69
77
  this.params = params
70
78
  this._viewId = nextViewId++
79
+ this._paramsJson = JSON.stringify(params ?? {})
80
+ tree().setString(this._h.id, "viewName", viewName)
81
+ tree().setString(this._h.id, "viewParams", this._paramsJson)
82
+ tree().setInt(this._h.id, "viewId", this._viewId)
83
+ registerDest(TREE_DEST_NATIVE, this._viewId, this)
71
84
  }
72
85
 
73
- /** @internal stable wire descriptor; hosts hand it back via router onChange (`_p` = this). */
74
- _viewDesc(): object {
75
- return this._vd ??= { type: "native", viewName: this.viewName, params: this.params, viewId: this._viewId, _p: this }
76
- }
86
+ /** @internal */
87
+ _dest(): DestTuple { return [TREE_DEST_NATIVE, this._viewId, this.viewName, this._paramsJson] }
77
88
 
78
89
  open(options?: PresentOptions): void {
79
- if (!_navSupported()) {
80
- throw new Error(`NativeView("${this.viewName}") is not supported on this host (requires _creatorUI.openView)`)
90
+ if (!tree().isViewSupported(this.viewName)) {
91
+ throw new Error(`NativeView("${this.viewName}") is not supported on this host (no registered view)`)
81
92
  }
82
93
  _bumpNavEpoch()
83
- _creatorUI.openView!(this._viewDesc(), options?.transition ?? "none")
94
+ tree().open(TREE_DEST_NATIVE, this._viewId, this.viewName, this._paramsJson, _transitionId(options?.transition, TRANSITION_NONE))
84
95
  _setCurrent(this)
85
96
  }
86
- close(): void {
97
+ close(options?: DismissOptions): void {
87
98
  _bumpNavEpoch()
88
- if (_navSupported()) _creatorUI.closeView!()
99
+ tree().close(TREE_DEST_NATIVE, this._viewId, _transitionId(options?.transition, TRANSITION_NONE))
89
100
  if (Presentable.current === this) _setCurrent(null)
90
101
  }
91
102
 
@@ -97,15 +108,47 @@ export class NativeViewElement extends Element<"native"> implements Presentable
97
108
  this.cl.push(callback)
98
109
  return this
99
110
  }
100
- onBackPressed(callback: () => void): this {
111
+ onBack(callback: () => void): this {
101
112
  this._backButtonCallback = callback
102
113
  return this
103
114
  }
115
+ /** @internal */
116
+ // The host has a view that can send events while the instance is presented OR mounted among a
117
+ // screen's children: findable by viewId while it is either (owned handles) — a promoted view that
118
+ // closes is still the embedded one.
119
+ private _reach(where: number, on: boolean): void {
120
+ const was = this._shown
121
+ this._shown = on ? was | where : was & ~where
122
+ if (was === 0 && this._shown !== 0) registerNativeView(this._viewId, this)
123
+ else if (was !== 0 && this._shown === 0) unregisterNativeView(this._viewId)
124
+ }
125
+ /** @internal */
126
+ _emitOpen(): void { this._reach(PRESENTED, true); for (const cb of this.ol.slice()) cb() }
127
+ /** @internal */
128
+ _emitClose(): void { this._reach(PRESENTED, false); for (const cb of this.cl.slice()) cb() }
129
+ /** @internal */
130
+ _pinned(): void { this._reach(MOUNTED, true) }
131
+ /** @internal */
132
+ _unpinned(): void { this._reach(MOUNTED, false) }
133
+ /** @internal */
134
+ override _freed(): void {
135
+ this._reach(MOUNTED, false)
136
+ super._freed()
137
+ }
138
+ /** @internal */
139
+ _emitBack(): boolean {
140
+ if (!this._backButtonCallback) return false
141
+ this._backButtonCallback()
142
+ return true
143
+ }
144
+
145
+ call(method: string, ...args: any[]): Promise<any> { return this._channelSend(method, args) }
104
146
 
105
- call(method: string, ...args: any[]): Promise<any> {
147
+ /** @internal One call over the channel (runtime/channel.ts). */
148
+ _channelSend(method: string, args: any[]): Promise<any> {
106
149
  return _channelCall(
107
- typeof _creatorUI !== "undefined" ? _creatorUI : undefined, "viewCall",
108
- `NativeView("${this.viewName}") is not supported on this host (requires _creatorUI.viewCall)`,
150
+ typeof _creatorTree !== "undefined" ? _creatorTree : undefined, "viewCall",
151
+ `NativeView("${this.viewName}") is not supported on this host (requires _creatorTree.viewCall)`,
109
152
  this._viewId, method, args)
110
153
  }
111
154
 
@@ -117,9 +160,9 @@ export class NativeViewElement extends Element<"native"> implements Presentable
117
160
  _channelOff(this.nvl, event, callback)
118
161
  return this
119
162
  }
120
- /** @internal host entry: deliver a native event (payload as a JSON string, or omitted). */
121
- _emitViewEvent(event: string, dataJson?: string): void {
122
- _channelEmit(this.nvl, event, dataJson)
163
+ /** @internal host entry: deliver a native event (its payload, or none). */
164
+ _emitViewEvent(event: string, data?: unknown): void {
165
+ _channelEmit(this.nvl, event, data)
123
166
  }
124
167
  }
125
168
 
@@ -133,10 +176,14 @@ export const NativeView: {
133
176
  (name: string, params?: object): NativeView
134
177
  /** Whether this host registered a `registerView` factory under `name`. */
135
178
  isSupported(name: string): boolean
179
+ /** The contract version of the host's view `name`: 0 when it has none, 1 for one written by hand. */
180
+ version(name: string): number
136
181
  } = /*#__PURE__*/ (() => {
137
182
  const factory = (name: string, params: object = {}): NativeView =>
138
183
  new NativeViewElement(name, params) as unknown as NativeView
139
184
  factory.isSupported = (name: string): boolean =>
140
- typeof _creatorUI !== "undefined" && typeof _creatorUI.isViewSupported === "function" && _creatorUI.isViewSupported(name)
185
+ typeof _creatorTree !== "undefined" && _creatorTree.isViewSupported(name)
186
+ factory.version = (name: string): number =>
187
+ typeof _creatorTree !== "undefined" ? _creatorTree.viewVersion(name) : 0
141
188
  return factory
142
189
  })()
package/src/ui/UI.ts CHANGED
@@ -1,5 +1,5 @@
1
- // Renderer-side contract + per-renderer status (DOM / canvas-ui / desktop) for every component
2
- // below is tracked in packages/canvas-ui/COMPONENTS.md.
1
+ // The UI surface: every element the SDK exports. The host side of each is the runtime's tree
2
+ // (docs/tree.md); the renderers implement HostUI over node ids.
3
3
  export { UIButton, type UIButtonStyle } from './UIButton'
4
4
  export { UIScreen, type UIScreenStyle } from './UIScreen'
5
5
  export { UISpacer, type UISpacerStyle } from './UISpacer'
@@ -25,7 +25,7 @@ export { Router } from './router'
25
25
  export { NativeView, type UINativeViewStyle } from './NativeView'
26
26
  // Presentable is both the interface and its runtime companion (Presentable.current).
27
27
  export { Presentable } from './presentable'
28
- export type { PresentOptions, Transition, TransitionName, TransitionSpec, TransitionTransform } from './presentable'
28
+ export type { DismissOptions, PresentOptions, Transition, TransitionName, TransitionPose, TransitionSpec } from './presentable'
29
29
 
30
30
  // Internal: memoized map for reactive children — the chisel `reactiveUi` pass rewrites
31
31
  // `list.map(fn)` inside children bindings to `__uiMap(list, fn, slot)` calls.
@@ -1,6 +1,7 @@
1
1
  import { ModalElement, type UIModalStyle } from "./UIModal"
2
2
  import type { UIWidget } from "./UIWidget"
3
3
  import { buildUI, type ChildrenFn, type UIChildArg, type UINodeChild } from "./UINode"
4
+ import { TREE_FLAG_DETENT } from "./tree"
4
5
 
5
6
  export type UIBottomSheetStyle = UIModalStyle
6
7
 
@@ -18,11 +19,11 @@ const SHEET_ANIM_MS = 250
18
19
  * modal machinery.
19
20
  *
20
21
  * ```ts
21
- * const actions = UIBottomSheet({ bgColor: "#1c1c1e", borderRadius: 16 }, [rows]) // content-sized
22
+ * const actions = UIBottomSheet(rows).style({ bgColor: "#1c1c1e", borderRadius: 16 }) // content-sized
22
23
  *
23
- * const mapSheet = UIBottomSheet({ bgColor: "#1c1c1e", borderRadius: 16 }, [results])
24
+ * const mapSheet = UIBottomSheet(results).style({ bgColor: "#1c1c1e", borderRadius: 16 })
24
25
  * .detents([0.3, 0.6, 1])
25
- * .onDetentChange(i => map.setPadding(...))
26
+ * .onDetent(i => map.setPadding(...))
26
27
  * ```
27
28
  */
28
29
  export interface UIBottomSheet extends UIWidget {
@@ -30,12 +31,12 @@ export interface UIBottomSheet extends UIWidget {
30
31
  * the last (highest) detent; set this before `show()`. Without a `detents()` call the sheet
31
32
  * is CONTENT-SIZED instead — one position, as tall as its children (up to the screen). */
32
33
  detents(list: number[]): this,
33
- /** Snap to a detent (animated). Clamped to the detents range; fires `onDetentChange`. */
34
+ /** Snap to a detent (animated). Clamped to the detents range; fires `onDetent`. */
34
35
  setDetent(index: number): void,
35
36
  /** Current detent index. */
36
37
  readonly detent: number,
37
38
  /** Fires on every settle: a finger snap on native hosts, or a `setDetent()` call. */
38
- onDetentChange(callback: (index: number) => void): this,
39
+ onDetent(callback: (index: number) => void): this,
39
40
 
40
41
  /** Mount and slide in to the current detent. No-op while already open. */
41
42
  show(): void,
@@ -56,10 +57,6 @@ export interface UIBottomSheet extends UIWidget {
56
57
  export class BottomSheetElement extends ModalElement {
57
58
  _detents: number[] = [1]
58
59
  _detent = 0
59
- /** Host-facing (the `sl`/`otl` contract): hosts read `dtl` at mount and call it with the
60
- * settled detent index for GESTURE settles only — programmatic `setDetent` fires JS-side.
61
- * `-1` = dragged below the lowest detent while dismissible. */
62
- readonly dtl: any[] = []
63
60
  readonly _detentListeners: ((index: number) => void)[] = []
64
61
 
65
62
  constructor(style: UIBottomSheetStyle, children: UINodeChild[] | ChildrenFn) {
@@ -70,11 +67,15 @@ export class BottomSheetElement extends ModalElement {
70
67
  // The inherited show/hide pose animates ONLY the scrim: sheet geometry rides the
71
68
  // sheetDetent style key, never the pose system.
72
69
  this._transition = { duration: SHEET_ANIM_MS }
73
- this.dtl.push((index: number) => {
74
- if (index < 0) { this.hide(); return }
75
- this._detent = index
76
- for (const callback of this._detentListeners) callback(index)
77
- })
70
+ this._addFlags(TREE_FLAG_DETENT)
71
+ }
72
+
73
+ /** @internal The host's GESTURE settles only (programmatic `setDetent` fires JS-side); `-1` =
74
+ * dragged below the lowest detent while dismissible. */
75
+ _emitDetent(index: number): void {
76
+ if (index < 0) { this.hide(); return }
77
+ this._detent = index
78
+ for (const callback of this._detentListeners) callback(index)
78
79
  }
79
80
 
80
81
  detents(list: number[]): this {
@@ -101,7 +102,7 @@ export class BottomSheetElement extends ModalElement {
101
102
  for (const callback of this._detentListeners) callback(clamped)
102
103
  }
103
104
 
104
- onDetentChange(callback: (index: number) => void): this {
105
+ onDetent(callback: (index: number) => void): this {
105
106
  this._detentListeners.push(callback)
106
107
  return this
107
108
  }
@@ -132,8 +133,6 @@ const makeBottomSheet = (style: any, children: any) => new BottomSheetElement(st
132
133
  * `.detents([...])` for snap positions. Same argument forms as `UIColumn`. */
133
134
  export function UIBottomSheet(...children: UIChildArg[]): UIBottomSheet;
134
135
  export function UIBottomSheet(children: UINodeChild[] | ChildrenFn): UIBottomSheet;
135
- /** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
136
- export function UIBottomSheet(style: UIBottomSheetStyle, children?: UINodeChild[] | ChildrenFn): UIBottomSheet;
137
136
  export function UIBottomSheet(...args: any[]): UIBottomSheet {
138
137
  return buildUI(args, null, makeBottomSheet)
139
138
  }