lecodes-sdk 2.2.8 → 2.2.10

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 (63) hide show
  1. package/dist/global.d.ts +42 -1
  2. package/dist/types/gl/Camera.d.ts +5 -2
  3. package/dist/types/gl/Node.d.ts +0 -7
  4. package/dist/types/gl/Scene.d.ts +6 -24
  5. package/dist/types/inject.d.ts +5 -1
  6. package/dist/types/plugin.d.ts +27 -0
  7. package/dist/types/plugins/gen/ar/sdk/ar.d.ts +228 -0
  8. package/dist/types/plugins/gen/ar/sdk/ar.gen.d.ts +73 -0
  9. package/dist/types/plugins/gen/ar/sdk/controls.d.ts +28 -0
  10. package/dist/types/plugins/gen/audio-recorder/sdk/audio-recorder.d.ts +43 -0
  11. package/dist/types/plugins/gen/audio-recorder/sdk/audio-recorder.gen.d.ts +47 -0
  12. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +6 -2
  13. package/dist/types/plugins/gen/speech/sdk/speech.d.ts +44 -0
  14. package/dist/types/plugins/gen/speech/sdk/speech.gen.d.ts +39 -0
  15. package/dist/types/plugins/gen/webrtc/sdk/webrtc.d.ts +3 -0
  16. package/dist/types/runtime/permissions.d.ts +16 -0
  17. package/dist/types/version.d.ts +1 -1
  18. package/dist/types.json +1 -1
  19. package/package.json +1 -1
  20. package/prompts/ar.md +81 -48
  21. package/prompts/dist/2d-game.md +1567 -0
  22. package/prompts/dist/3d-app.md +1712 -0
  23. package/prompts/dist/ar-app.md +1585 -0
  24. package/prompts/dist/concept.md +49 -0
  25. package/prompts/dist/design.md +521 -0
  26. package/prompts/dist/ui-app.md +1175 -0
  27. package/prompts/index.md +3 -1
  28. package/src/animate/tween/Timeline.ts +175 -175
  29. package/src/bridges/device.d.ts +20 -6
  30. package/src/bridges/gl.d.ts +33 -19
  31. package/src/compile/header.ts +11 -4
  32. package/src/gl/Camera.ts +5 -2
  33. package/src/gl/CameraPlace.ts +52 -52
  34. package/src/gl/Light.ts +257 -257
  35. package/src/gl/Mesh.ts +120 -120
  36. package/src/gl/Node.ts +0 -8
  37. package/src/gl/Scene.ts +14 -81
  38. package/src/gl/animation/DynamicBone.ts +482 -482
  39. package/src/gl/scenarios.ts +291 -291
  40. package/src/inject.ts +13 -1
  41. package/src/plugin.ts +45 -2
  42. package/src/plugins/gen/ar/contract.d.ts +118 -0
  43. package/src/plugins/gen/ar/sdk/ar.gen.ts +112 -0
  44. package/src/plugins/gen/ar/sdk/ar.ts +699 -0
  45. package/src/plugins/gen/ar/sdk/controls.ts +148 -0
  46. package/src/plugins/gen/audio-recorder/contract.d.ts +64 -0
  47. package/src/plugins/gen/audio-recorder/sdk/audio-recorder.gen.ts +76 -0
  48. package/src/plugins/gen/audio-recorder/sdk/audio-recorder.ts +113 -0
  49. package/src/plugins/gen/geolocation/contract.d.ts +7 -5
  50. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +6 -2
  51. package/src/plugins/gen/speech/contract.d.ts +67 -0
  52. package/src/plugins/gen/speech/sdk/speech.gen.ts +64 -0
  53. package/src/plugins/gen/speech/sdk/speech.ts +104 -0
  54. package/src/plugins/gen/webrtc/sdk/webrtc.ts +19 -14
  55. package/src/runtime/permissions.ts +40 -0
  56. package/src/server/auth/host.ts +1 -1
  57. package/src/ui/presentable.ts +5 -0
  58. package/src/ui/router.ts +4 -3
  59. package/src/version.ts +1 -1
  60. package/dist/types/gl/controls.d.ts +0 -19
  61. package/dist/types/plugins/permission.d.ts +0 -1
  62. package/src/gl/controls.ts +0 -95
  63. package/src/plugins/permission.ts +0 -8
package/src/plugin.ts CHANGED
@@ -12,16 +12,41 @@ import { File as HostFile } from "./runtime/fetch"
12
12
  import { ServiceClient } from "./runtime/service"
13
13
  import { NativeView, NativeViewElement } from "./ui/NativeView"
14
14
  import { _navEpoch } from "./ui/presentable"
15
- import { _requestCameraPermission } from "./plugins/permission"
15
+ import { _ensurePermission } from "./runtime/permissions"
16
+ export { permissions, type PermissionKind, type PermissionState } from "./runtime/permissions"
16
17
  import { appEventsOff, appEventsOn } from "./runtime/appEvents"
18
+ import { nodeOf, type Node } from "./gl/Node"
19
+ import type { Scene } from "./gl/Scene"
20
+ /** @internal Ask for an OS permission and reject unless it is granted: a wrapper's preparation, while
21
+ * the current destination stays on screen (ARScene's camera). */
22
+ export { _ensurePermission }
17
23
 
18
24
  export type { PresentOptions } from "./ui/presentable"
19
25
  // The SDK UI a wrapper builds its own chrome with (the browser plugin's bar over an embedded page) and
20
26
  // the navigation it presents through. Re-exported, never copied: chisel keys on these names.
21
27
  export { Presentable, Router, UIBox, UIButton, UIColumn, UIRow, UIScreen, UIText } from "./ui/UI"
22
28
  export { theme } from "./ui/theme"
29
+ export { Emitter } from "./core/events"
23
30
  export type { NativeView } from "./ui/NativeView"
24
31
  export type { FetchResponse } from "./runtime/fetch"
32
+ // The 3D a wrapper builds on (the "ar" plugin's ARScene — docs/plans/ar-plugin-plan.md): the scene it
33
+ // extends, the node its anchors are, the mesh it draws a plane's grid with, the math of a pose, the
34
+ // touch a placement gesture follows, the System its per-frame work rides on (the scene's first frame).
35
+ // Re-exported, never copied, as the UI above.
36
+ export { Scene, type SceneOptions } from "./gl/Scene"
37
+ export { System } from "./core/Aspect"
38
+ export { Node, type NodeEvents } from "./gl/Node"
39
+ export { Mesh } from "./gl/Mesh"
40
+ export { Geometry } from "./gl/Geometry"
41
+ export { Material } from "./gl/Material"
42
+ export { Vec2, Vec3, type Vec3Like } from "./math/vec"
43
+ export { Quat, type QuatLike } from "./math/quat"
44
+ export { Mat4 } from "./math/mat4"
45
+ export type { ClickEvent, TouchStartEvent } from "./runtime/touch"
46
+ export type { DismissOptions } from "./ui/presentable"
47
+ /** @internal The navigation epoch: a destination that prepares before it presents (ARScene) reads it
48
+ * before its awaits and gives up when another navigation moved it meanwhile. */
49
+ export { _navEpoch }
25
50
 
26
51
  // ---- the contract's vocabulary ---------------------------------------------------------------------
27
52
 
@@ -35,6 +60,17 @@ export type f64 = number
35
60
  * never enter the JS heap. For bytes the app must read itself, a contract says `Uint8Array`. */
36
61
  export type File = HostFile
37
62
 
63
+ /** A 3D node of the app (`Node`) a plugin drives — an AR anchor, the camera: its entity id on the
64
+ * wire (u32, `node._h.id`); the native half receives `Entity` (a struct over the id) and moves it
65
+ * through the host's gl facade (`engine.gl` / `host.gl`), never creating or freeing it. One the half
66
+ * names back (an event's) is the app's own Node again. */
67
+ export type Entity = Node
68
+
69
+ /** A 3D scene of the app (`Scene`) a plugin drives — its camera's projection, its background: the
70
+ * scene id on the wire (`scene._id`); the native half receives `Scene3D`. From the app to the host
71
+ * only: a half never names a scene back. */
72
+ export type Scene3D = Scene
73
+
38
74
  /** Any JSON value, opaque to the generator (GeoJSON, a developer's own payload): it crosses as is
39
75
  * and the native half receives the platform's untyped value. */
40
76
  export type Json = null | boolean | number | string | Json[] | { [key: string]: Json }
@@ -175,6 +211,13 @@ export const _toFile = (wire: { systemId: number, name: string, size: number }):
175
211
  export const _fromFile = (file: File): { systemId: number, name: string, size: number } =>
176
212
  ({ systemId: file._h.id, name: file.name, size: file.size })
177
213
 
214
+ /** @internal The generated wrapper's: an Entity / a Scene3D the app hands a half crosses as its id… */
215
+ export const _fromEntity = (node: Entity): number => node._h.id
216
+ export const _fromScene3D = (scene: Scene3D): number => scene._id
217
+ /** @internal …and an Entity a half names back is the app's own Node (the pinned one, the app's live
218
+ * handle, else a wrapper over the engine's id). */
219
+ export const _toEntity = (id: number): Entity => nodeOf(id)
220
+
178
221
  // ---- for the hand-written part ---------------------------------------------------------------------
179
222
 
180
223
  /** Hear the app's "url" events (a deep link while the app runs — an OAuth redirect, a checkout's
@@ -197,7 +240,7 @@ export const prepareCamera = async (view: ViewChannel<any>): Promise<void> => {
197
240
  throw new Error(`${view._iface} isn't available on this host — it needs a registered "${view.viewName}" view.`)
198
241
  }
199
242
  const epoch = _navEpoch()
200
- await _requestCameraPermission()
243
+ await _ensurePermission("camera")
201
244
  if (_navEpoch() !== epoch) {
202
245
  throw new Error(`${view._iface}.open() superseded: another destination was opened while preparing`)
203
246
  }
@@ -0,0 +1,118 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/ar/contract.d.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // ar — world tracking over the device camera: ARKit on iOS, ARCore on Android, the camera under the
4
+ // scene turned by the sensors on the web. The app's side is ARScene (sdk/ar.ts), a Scene whose
5
+ // camera and background the session drives every frame through the host's gl facade.
6
+ // A session is ONE AR scene: ARScene opens it with the scene and its camera entity as params and
7
+ // closes it with the scene. `@permission camera` on start: the host asks, the halves never do.
8
+ import type { Service, File, Entity, Scene3D, f64 } from "../../../plugin"
9
+
10
+ export type ARMode = "world" | "images"
11
+ export type PlaneDetection = "horizontal" | "vertical" | "both" | "none"
12
+ export type WorldAlignment = "gravity" | "gravityAndHeading"
13
+ export type TrackingState = "notAvailable" | "limited" | "normal"
14
+ export type TrackingReason = "initializing" | "excessiveMotion" | "insufficientFeatures" | "relocalizing"
15
+ export type PlaneType = "horizontal" | "vertical"
16
+ export type PlaneChange = "added" | "updated" | "removed"
17
+ export type HitTarget = "planes" | "estimated" | "any"
18
+ export type ARErrorCode = "unavailable" | "camera" | "failed"
19
+
20
+ export interface Vec3 { x: f64, y: f64, z: f64 }
21
+ export interface Quat { x: f64, y: f64, z: f64, w: f64 }
22
+ /** A pose in the scene's world: metres, Y up. */
23
+ export interface Pose { position: Vec3, rotation: Quat }
24
+
25
+ export interface ImageTarget {
26
+ image: File
27
+ /** The printed width, metres: the size the session starts from (the height by the image's
28
+ * proportions) — the framework refines it (the `image` event's `extent`). */
29
+ physicalWidth: f64
30
+ /** The entity that follows the image (its Anchor node). */
31
+ entity: Entity
32
+ }
33
+
34
+ export interface StartOptions {
35
+ /** Default "world". "images": image tracking only (no planes, no hit test; iOS runs
36
+ * ARImageTrackingConfiguration, which is lighter). */
37
+ mode?: ARMode
38
+ /** Default "horizontal". Ignored in "images". */
39
+ planes?: PlaneDetection
40
+ images?: ImageTarget[]
41
+ /** Default "gravity". "gravityAndHeading": -Z points north (the compass). */
42
+ worldAlignment?: WorldAlignment
43
+ /** Deliver `light` events. Default false. */
44
+ lightEstimation?: boolean
45
+ /** Default true. */
46
+ autoFocus?: boolean
47
+ }
48
+
49
+ export interface HitResult {
50
+ pose: Pose
51
+ /** "plane": on a detected plane (`plane` = its id); "estimated": on a point of the feature
52
+ * cloud / an estimated plane. */
53
+ type: "plane" | "estimated"
54
+ plane?: f64
55
+ /** Metres from the camera. */
56
+ distance: f64
57
+ }
58
+
59
+ /** Both are ARScene's, always sent. Optional because a channel's params are (the generator's rule: a
60
+ * param a half cannot read has no one to reject to); a session opened without them rejects `start`
61
+ * with "failed". */
62
+ export interface ARParams {
63
+ scene?: Scene3D
64
+ /** The scene's camera entity: the session sets its matrix and the scene's projection each frame. */
65
+ camera?: Entity
66
+ }
67
+
68
+ export interface AREvents {
69
+ /** The tracking quality; the first event of a session. */
70
+ tracking: { state: TrackingState, reason?: TrackingReason }
71
+ /** A detected plane: added, moved / grown (throttled: a change over 1 cm, at most 10 per second),
72
+ * or gone (`mergedInto` = the plane that absorbed it, ARCore). `pose` = the plane's centre, its
73
+ * Y the normal; `extent` = [width, height] metres; `polygon` = the boundary as x,z pairs in the
74
+ * plane's space. */
75
+ plane: { change: PlaneChange, id: f64, type: PlaneType, pose: Pose, extent: f64[], polygon: f64[], mergedInto?: f64 }
76
+ /** An image target found, lost, or its size refined. While tracked its entity is driven: a rigid
77
+ * pose in metres, no scale — the image's centre, Y out of it, the image in XZ (X along its width,
78
+ * Z towards its bottom). `extent` = [width, height] metres, the framework's estimate (sent again
79
+ * when it changes by a millimetre, at most 10 per second); the last one when lost. */
80
+ image: { entity: Entity, tracked: boolean, extent: f64[] }
81
+ /** An anchor made by `anchor()` lost or regained after a relocalization. */
82
+ anchor: { entity: Entity, tracked: boolean }
83
+ /** The estimated light (`lightEstimation`): lumens-ish ambient intensity (1000 = ARKit's
84
+ * neutral), colour temperature in kelvin; at most 10 per second. */
85
+ light: { intensity: f64, temperature: f64 }
86
+ /** The session failed and is over: "unavailable" (no ARKit / ARCore support, ARCore not
87
+ * installed and declined), "camera" (the camera could not be used), "failed". */
88
+ error: { code: ARErrorCode, message: string }
89
+ }
90
+
91
+ export interface AR extends Service<"ar", ARParams, AREvents> {
92
+ /** Start tracking; resolves at the first frame. A second start rejects "busy".
93
+ * @permission camera
94
+ * @rejects busy unavailable failed */
95
+ start(options?: StartOptions): Promise<void>
96
+ /** Stop tracking: the camera is released, the scene's projection and background cleared. Not
97
+ * running is not an error. */
98
+ stop(): Promise<void>
99
+ /** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
100
+ * planes and the anchors stay); an image no longer in the set stops driving its entity (its
101
+ * `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
102
+ * `StartOptions.images`.
103
+ * @rejects notRunning failed */
104
+ setImages(images: ImageTarget[]): Promise<void>
105
+ /** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
106
+ * `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
107
+ * a host without surfaces (the web).
108
+ * @rejects notRunning */
109
+ hitTest(x: f64, y: f64, target?: HitTarget): Promise<HitResult[]>
110
+ /** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
111
+ * as the world map refines). @rejects notRunning */
112
+ anchor(entity: Entity, pose: Pose): Promise<void>
113
+ /** Stop driving `entity` (its matrix stays where it was). */
114
+ release(entity: Entity): Promise<void>
115
+ /** The latest camera frame as a JPEG (the raw image, not the composite — that is
116
+ * `Scene.capture()`). @rejects notRunning failed */
117
+ captureFrame(): Promise<File>
118
+ }
@@ -0,0 +1,112 @@
1
+ // GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/ar/sdk/ar.gen.ts — do not edit here.
2
+ // Change it in the plugin repo and run `bun run vendor:plugins`; `bun run check:plugins` fails when this copy is stale.
3
+ // GENERATED by `lecodes plugin gen` from contract.d.ts — do not edit.
4
+ // ar: the service "ar", contract version 1.
5
+
6
+ import { ServiceChannel, _fromEntity, _fromFile, _fromScene3D, _toEntity, _toFile, type Entity, type EventReaders, type File } from "../../../../plugin"
7
+ import type { AREvents, ARParams, HitResult, HitTarget, ImageTarget, Pose, StartOptions } from "../contract"
8
+
9
+ /** What the halves register under. */
10
+ export const NAME = "ar"
11
+ /** The contract's version: the highest `@since` of its members. */
12
+ export const VERSION = 1
13
+
14
+ const _fromStartOptions = (v: StartOptions): any => ({ ...v, images: v.images === undefined ? undefined : v.images.map((x: any) => _fromImageTarget(x)) })
15
+ const _fromImageTarget = (v: ImageTarget): any => ({ ...v, image: _fromFile(v.image), entity: _fromEntity(v.entity) })
16
+ const _fromARParams = (v: ARParams): any => ({ ...v, scene: v.scene === undefined ? undefined : _fromScene3D(v.scene), camera: v.camera === undefined ? undefined : _fromEntity(v.camera) })
17
+
18
+ /** The events whose payload holds a File: read before a listener sees it. */
19
+ export const READERS: EventReaders = {
20
+ image: (w: any) => ({ ...w, entity: _toEntity(w.entity) }),
21
+ anchor: (w: any) => ({ ...w, entity: _toEntity(w.entity) }),
22
+ }
23
+
24
+ // ---- the wire, call by call ----------------------------------------------------------------------
25
+
26
+ /** Start tracking; resolves at the first frame. A second start rejects "busy".
27
+ * Needs the "camera" permission: the host asks before the call, "denied" when refused.
28
+ * Rejects with "denied" / "busy" / "unavailable" / "failed". */
29
+ export const start = (service: ServiceChannel<AREvents>, options?: StartOptions): Promise<void> =>
30
+ service._call("start", [options === undefined ? undefined : _fromStartOptions(options)])
31
+
32
+ /** Stop tracking: the camera is released, the scene's projection and background cleared. Not
33
+ * running is not an error. */
34
+ export const stop = (service: ServiceChannel<AREvents>): Promise<void> =>
35
+ service._call("stop", [])
36
+
37
+ /** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
38
+ * planes and the anchors stay); an image no longer in the set stops driving its entity (its
39
+ * `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
40
+ * `StartOptions.images`.
41
+ * Rejects with "notRunning" / "failed". */
42
+ export const setImages = (service: ServiceChannel<AREvents>, images: ImageTarget[]): Promise<void> =>
43
+ service._call("setImages", [images.map((x: any) => _fromImageTarget(x))])
44
+
45
+ /** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
46
+ * `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
47
+ * a host without surfaces (the web).
48
+ * Rejects with "notRunning". */
49
+ export const hitTest = (service: ServiceChannel<AREvents>, x: number, y: number, target?: HitTarget): Promise<HitResult[]> =>
50
+ service._call("hitTest", [x, y, target])
51
+
52
+ /** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
53
+ * as the world map refines).
54
+ * Rejects with "notRunning". */
55
+ export const anchor = (service: ServiceChannel<AREvents>, entity: Entity, pose: Pose): Promise<void> =>
56
+ service._call("anchor", [_fromEntity(entity), pose])
57
+
58
+ /** Stop driving `entity` (its matrix stays where it was). */
59
+ export const release = (service: ServiceChannel<AREvents>, entity: Entity): Promise<void> =>
60
+ service._call("release", [_fromEntity(entity)])
61
+
62
+ /** The latest camera frame as a JPEG (the raw image, not the composite — that is
63
+ * `Scene.capture()`).
64
+ * Rejects with "notRunning" / "failed". */
65
+ export const captureFrame = (service: ServiceChannel<AREvents>): Promise<File> =>
66
+ service._call("captureFrame", []).then((w: any) => _toFile(w))
67
+
68
+ // ---- what the host's half has --------------------------------------------------------------------
69
+
70
+ /** A method of the contract. */
71
+ export type Method = "start" | "stop" | "setImages" | "hitTest" | "anchor" | "release" | "captureFrame"
72
+
73
+ /** Whether this host has the service "ar" — and, given a method, a half new enough for it
74
+ * (a method added after the first version of the contract is tagged `@since`). */
75
+ export const supports = (method?: Method): boolean =>
76
+ ServiceChannel.supports(NAME) // every method is of version 1
77
+
78
+ // ---- the client -----------------------------------------------------------------------------------
79
+
80
+ /** The service "ar" with the wire's methods as its own. */
81
+ export class ARBase extends ServiceChannel<AREvents> {
82
+ constructor(params: ARParams = {}) { super("AR", NAME, _fromARParams(params), READERS) }
83
+
84
+ /** Start tracking; resolves at the first frame. A second start rejects "busy".
85
+ * Needs the "camera" permission: the host asks before the call, "denied" when refused.
86
+ * Rejects with "denied" / "busy" / "unavailable" / "failed". */
87
+ start(options?: StartOptions): Promise<void> { return start(this, options) }
88
+ /** Stop tracking: the camera is released, the scene's projection and background cleared. Not
89
+ * running is not an error. */
90
+ stop(): Promise<void> { return stop(this) }
91
+ /** Replace the set of image targets of a RUNNING session without restarting it (the tracking, the
92
+ * planes and the anchors stay); an image no longer in the set stops driving its entity (its
93
+ * `image` event says `tracked: false` once if it was tracked). Before `start`, the images are
94
+ * `StartOptions.images`.
95
+ * Rejects with "notRunning" / "failed". */
96
+ setImages(images: ImageTarget[]): Promise<void> { return setImages(this, images) }
97
+ /** What is under a point of the scene's view — in its touches' space (a scene touch's `clientX` /
98
+ * `clientY`: the units of the gl facade's viewport) — nearest first. Empty when nothing is, or on
99
+ * a host without surfaces (the web).
100
+ * Rejects with "notRunning". */
101
+ hitTest(x: number, y: number, target?: HitTarget): Promise<HitResult[]> { return hitTest(this, x, y, target) }
102
+ /** Pin `entity` to `pose`: the host's anchor drives its matrix from now on (and keeps it true
103
+ * as the world map refines).
104
+ * Rejects with "notRunning". */
105
+ anchor(entity: Entity, pose: Pose): Promise<void> { return anchor(this, entity, pose) }
106
+ /** Stop driving `entity` (its matrix stays where it was). */
107
+ release(entity: Entity): Promise<void> { return release(this, entity) }
108
+ /** The latest camera frame as a JPEG (the raw image, not the composite — that is
109
+ * `Scene.capture()`).
110
+ * Rejects with "notRunning" / "failed". */
111
+ captureFrame(): Promise<File> { return captureFrame(this) }
112
+ }