lecodes-sdk 1.2.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 (260) 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 +8 -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/Scene.d.ts +41 -13
  25. package/dist/types/gl/Texture.d.ts +1 -1
  26. package/dist/types/gl/animation/Locomotion.d.ts +8 -1
  27. package/dist/types/inject.d.ts +11 -11
  28. package/dist/types/inject.editor.d.ts +1 -0
  29. package/dist/types/net/core.d.ts +7 -0
  30. package/dist/types/plugin.d.ts +76 -0
  31. package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
  32. package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
  33. package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
  34. package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
  35. package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
  36. package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
  37. package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
  38. package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
  39. package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
  40. package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
  41. package/dist/types/runtime/app.d.ts +9 -2
  42. package/dist/types/runtime/fetch.d.ts +2 -0
  43. package/dist/types/runtime/input.d.ts +1 -1
  44. package/dist/types/runtime/media.d.ts +6 -10
  45. package/dist/types/runtime/misc.d.ts +4 -1
  46. package/dist/types/runtime/net.d.ts +3 -2
  47. package/dist/types/runtime/touch.d.ts +32 -0
  48. package/dist/types/scene/defineScene.d.ts +43 -2
  49. package/dist/types/scene/editor.d.ts +52 -0
  50. package/dist/types/scene/gizmos.d.ts +7 -4
  51. package/dist/types/ui/NativeView.d.ts +6 -4
  52. package/dist/types/ui/UI.d.ts +1 -1
  53. package/dist/types/ui/UIBottomSheet.d.ts +6 -12
  54. package/dist/types/ui/UIButton.d.ts +12 -14
  55. package/dist/types/ui/UIContainer.d.ts +0 -6
  56. package/dist/types/ui/UIImage.d.ts +1 -4
  57. package/dist/types/ui/UIInput.d.ts +8 -24
  58. package/dist/types/ui/UIModal.d.ts +0 -2
  59. package/dist/types/ui/UINode.d.ts +70 -56
  60. package/dist/types/ui/UIPager.d.ts +28 -27
  61. package/dist/types/ui/UIPopover.d.ts +0 -2
  62. package/dist/types/ui/UIScreen.d.ts +12 -17
  63. package/dist/types/ui/UIScrollable.d.ts +1 -4
  64. package/dist/types/ui/UIText.d.ts +0 -2
  65. package/dist/types/ui/UIVideo.d.ts +3 -5
  66. package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
  67. package/dist/types/ui/UIWidget.d.ts +10 -9
  68. package/dist/types/ui/colorKeys.gen.d.ts +9 -0
  69. package/dist/types/ui/presentable.d.ts +46 -32
  70. package/dist/types/ui/router.d.ts +18 -7
  71. package/dist/types/ui/styleColor.d.ts +1 -0
  72. package/dist/types/ui/transitions.d.ts +18 -0
  73. package/dist/types/ui/tree.d.ts +75 -0
  74. package/dist/types/version.d.ts +10 -0
  75. package/dist/types.json +1 -1
  76. package/package.json +12 -3
  77. package/prompts/2d.md +2 -6
  78. package/prompts/3d.md +1 -5
  79. package/prompts/README.md +1 -1
  80. package/prompts/canvas.md +9 -8
  81. package/prompts/compose.ts +1 -1
  82. package/prompts/core.md +3 -3
  83. package/prompts/design.md +1 -1
  84. package/prompts/dist/2d-game.md +473 -239
  85. package/prompts/dist/3d-app.md +553 -205
  86. package/prompts/dist/ar-app.md +435 -202
  87. package/prompts/dist/design.md +113 -95
  88. package/prompts/dist/ui-app.md +386 -170
  89. package/prompts/ui-design.md +2 -3
  90. package/prompts/ui.md +45 -37
  91. package/src/animate/tween/Animation.ts +34 -150
  92. package/src/animate/tween/Timeline.ts +175 -175
  93. package/src/animate/tween/animateValue.ts +6 -3
  94. package/src/animate/tween/easing.ts +10 -3
  95. package/src/animate/tween/spec.ts +41 -15
  96. package/src/audio/Sound.ts +3 -3
  97. package/src/audio/audio.ts +2 -1
  98. package/src/bridges/2d.d.ts +317 -0
  99. package/src/bridges/app.d.ts +91 -0
  100. package/src/bridges/audio.d.ts +97 -0
  101. package/src/bridges/canvas.d.ts +79 -0
  102. package/src/bridges/device.d.ts +72 -0
  103. package/src/bridges/fetch.d.ts +80 -0
  104. package/src/bridges/files.d.ts +70 -0
  105. package/src/bridges/gl.d.ts +1133 -0
  106. package/src/bridges/input.d.ts +72 -0
  107. package/src/bridges/media.d.ts +55 -0
  108. package/src/bridges/nav.d.ts +71 -0
  109. package/src/bridges/net.d.ts +52 -0
  110. package/src/bridges/service.d.ts +52 -0
  111. package/src/bridges/socket.d.ts +31 -0
  112. package/src/bridges/storage.d.ts +33 -0
  113. package/src/bridges/tree.d.ts +301 -0
  114. package/src/bridges/types.d.ts +49 -0
  115. package/src/canvas/Canvas.ts +114 -159
  116. package/src/canvas/gen/cssColor.ts +224 -0
  117. package/src/canvas/gen/recorder.ts +212 -0
  118. package/src/canvas/gen/spec.ts +201 -0
  119. package/src/chisel.ts +193 -0
  120. package/src/compile/assetMacro.ts +1 -1
  121. package/src/compile/bundler.ts +11 -2
  122. package/src/compile/compileProject.ts +43 -4
  123. package/src/compile/fontMacro.ts +3 -4
  124. package/src/compile/header.ts +26 -5
  125. package/src/compile/index.ts +3 -1
  126. package/src/compile/liteMaterial.ts +1 -1
  127. package/src/compile/sceneEditor.ts +11 -26
  128. package/src/core/color.ts +73 -30
  129. package/src/core/pins.ts +51 -0
  130. package/src/core/signals.ts +8 -1
  131. package/src/g2/CharacterController2D.ts +3 -3
  132. package/src/g2/Node2D.ts +57 -39
  133. package/src/g2/Physics2D.ts +2 -2
  134. package/src/g2/Scene2D.ts +35 -23
  135. package/src/g2/Texture2D.ts +1 -1
  136. package/src/g2/loop.ts +4 -4
  137. package/src/gl/CameraPlace.ts +52 -52
  138. package/src/gl/Foliage.ts +72 -17
  139. package/src/gl/Geometry.ts +1 -2
  140. package/src/gl/Light.ts +10 -0
  141. package/src/gl/Lightmap.ts +45 -29
  142. package/src/gl/Material.ts +95 -51
  143. package/src/gl/Mesh.ts +120 -120
  144. package/src/gl/Model.ts +21 -16
  145. package/src/gl/Node.ts +91 -24
  146. package/src/gl/Particles.ts +1 -1
  147. package/src/gl/Scene.ts +100 -44
  148. package/src/gl/Texture.ts +8 -7
  149. package/src/gl/animation/AnimationClip.ts +1 -1
  150. package/src/gl/animation/DynamicBone.ts +482 -482
  151. package/src/gl/animation/Locomotion.ts +8 -3
  152. package/src/gl/nav/NavMesh.ts +3 -4
  153. package/src/gl/physics/Physics.ts +2 -2
  154. package/src/gl/physics/physicsEvents.ts +3 -3
  155. package/src/gl/scenarios.ts +291 -291
  156. package/src/gl/terrain/Terrain.ts +4 -5
  157. package/src/gl/touch.ts +14 -15
  158. package/src/host.d.ts +2 -3
  159. package/src/inject.editor.ts +7 -0
  160. package/src/inject.ts +13 -16
  161. package/src/net/core.ts +6 -5
  162. package/src/net/index.ts +1 -1
  163. package/src/net/replication.ts +1 -1
  164. package/src/plugin.ts +191 -0
  165. package/src/plugins/gen/camera/contract.d.ts +27 -0
  166. package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
  167. package/src/plugins/gen/camera/sdk/camera.ts +57 -0
  168. package/src/plugins/gen/geolocation/contract.d.ts +50 -0
  169. package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
  170. package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
  171. package/src/plugins/gen/map/contract.d.ts +144 -0
  172. package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
  173. package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
  174. package/src/plugins/gen/push/contract.d.ts +61 -0
  175. package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
  176. package/src/plugins/gen/push/sdk/push.ts +105 -0
  177. package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
  178. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
  179. package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
  180. package/src/plugins/permission.ts +5 -4
  181. package/src/runtime/app.ts +20 -9
  182. package/src/runtime/appEvents.ts +5 -4
  183. package/src/runtime/channel.ts +18 -15
  184. package/src/runtime/clipboard.ts +4 -3
  185. package/src/runtime/datetime.ts +2 -1
  186. package/src/runtime/device.ts +17 -15
  187. package/src/runtime/fetch.ts +30 -20
  188. package/src/runtime/files.ts +16 -15
  189. package/src/runtime/input.ts +12 -10
  190. package/src/runtime/media.ts +50 -46
  191. package/src/runtime/misc.ts +7 -3
  192. package/src/runtime/net.ts +8 -7
  193. package/src/runtime/rpc.ts +1 -3
  194. package/src/runtime/service.ts +19 -14
  195. package/src/runtime/share.ts +4 -3
  196. package/src/runtime/storage.ts +6 -4
  197. package/src/runtime/touch.ts +32 -0
  198. package/src/scene/defineScene.ts +61 -365
  199. package/src/scene/editor.ts +408 -0
  200. package/src/scene/editorPlugins.ts +3 -3
  201. package/src/scene/gizmos.ts +15 -9
  202. package/src/server/db/marci/query.ts +1 -1
  203. package/src/server/host.ts +1 -1
  204. package/src/server/runtime.ts +1 -1
  205. package/src/ui/NativeView.ts +72 -25
  206. package/src/ui/UI.ts +3 -3
  207. package/src/ui/UIBottomSheet.ts +16 -17
  208. package/src/ui/UIButton.ts +54 -16
  209. package/src/ui/UIContainer.ts +0 -6
  210. package/src/ui/UIImage.ts +34 -30
  211. package/src/ui/UIInput.ts +29 -37
  212. package/src/ui/UIModal.ts +1 -3
  213. package/src/ui/UINode.ts +347 -297
  214. package/src/ui/UIPager.ts +93 -78
  215. package/src/ui/UIPopover.ts +0 -2
  216. package/src/ui/UIScreen.ts +41 -36
  217. package/src/ui/UIScrollable.ts +19 -13
  218. package/src/ui/UISpacer.ts +1 -1
  219. package/src/ui/UITabs.ts +8 -6
  220. package/src/ui/UIText.ts +7 -17
  221. package/src/ui/UIVideo.ts +30 -27
  222. package/src/ui/UIVirtualizedList.ts +58 -59
  223. package/src/ui/UIWidget.ts +38 -18
  224. package/src/ui/colorKeys.gen.ts +37 -0
  225. package/src/ui/fonts.ts +2 -2
  226. package/src/ui/presentable.ts +59 -42
  227. package/src/ui/router.ts +52 -35
  228. package/src/ui/styleColor.ts +56 -0
  229. package/src/ui/theme.ts +7 -6
  230. package/src/ui/transitions.ts +249 -0
  231. package/src/ui/tree.ts +346 -0
  232. package/src/version.ts +24 -0
  233. package/tests/helpers/engineWorld.ts +11 -0
  234. package/tests/helpers/fakeTree.ts +353 -0
  235. package/tests/helpers/hostStubs.ts +31 -0
  236. package/tests/helpers/index.ts +14 -0
  237. package/tests/helpers/memoryMarci.ts +124 -0
  238. package/tests/helpers/phases.ts +23 -0
  239. package/tests/helpers/preload.ts +18 -0
  240. package/tests/helpers/stubApp.ts +2 -0
  241. package/tests/helpers/stubDevice.ts +2 -0
  242. package/tests/helpers/stubFetch.ts +2 -0
  243. package/tests/helpers/stubInput.ts +2 -0
  244. package/dist/inject.js +0 -4629
  245. package/dist/types/core/registry.d.ts +0 -7
  246. package/dist/types/plugins/camera.d.ts +0 -25
  247. package/dist/types/plugins/push.d.ts +0 -46
  248. package/src/bridges.d.ts +0 -1769
  249. package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
  250. package/src/compile/__tests__/assetMacro.test.ts +0 -100
  251. package/src/compile/__tests__/assetName.test.ts +0 -55
  252. package/src/compile/__tests__/compile.test.ts +0 -310
  253. package/src/compile/__tests__/detectEntry.test.ts +0 -151
  254. package/src/compile/__tests__/fontMacro.test.ts +0 -199
  255. package/src/compile/__tests__/serverSplit.test.ts +0 -27
  256. package/src/core/__tests__/stateMachine.test.ts +0 -132
  257. package/src/core/registry.ts +0 -23
  258. package/src/plugins/camera.ts +0 -81
  259. package/src/plugins/push.ts +0 -132
  260. package/src/plugins/qr.ts +0 -73
@@ -1,13 +1,15 @@
1
- // Audio / video playback over the host. Ported from worker/src/MediaPlayer.ts, with the listener
2
- // boilerplate folded into the shared Emitter. VideoPlayer.texture (a 3D Texture) is wired in Phase 4
3
- // when the Texture type exists — see gl/.
1
+ // Audio / video playback over the host's `_creatorMedia` bridge (absent on hosts without media
2
+ // players — the players are inert there). The listener boilerplate is folded into the shared Emitter.
4
3
  //
5
- // The host no longer routes end-of-playback events back by id through a Registry: each player hands
6
- // the host a `_finished` callback at creation time. The host holds the JS closure directly — valid
7
- // on every backend (web JS, Android quickjs, iOS JavaScriptCore) — and invokes it when a play-through
8
- // completes, so there is no id table to keep in sync.
4
+ // Ownership (owned handles, docs/plans/hosts-unification-plan.md phase 10): a player is an owned resource
5
+ // — `_h` is the handle the runtime returned; the runtime counts a PLAYING player as natively owned.
6
+ // So `new AudioPlayer(src).play()` in a handler, reference dropped, plays to its end and is released
7
+ // then; a paused player you stop referencing is released at once. There is nothing to dispose. The
8
+ // end-of-playback report comes by id through ONE emitter per world; a playing player is pinned so
9
+ // the report finds it.
9
10
 
10
11
  import { Emitter } from "../core/events"
12
+ import { Pins } from "../core/pins"
11
13
  import { Texture } from "../gl/Texture"
12
14
 
13
15
  type MediaEvents = {
@@ -17,83 +19,84 @@ type MediaEvents = {
17
19
  completed: () => void
18
20
  }
19
21
 
22
+ /** The players native owns right now (playing), by id — what an end-of-playback report resolves to. */
23
+ const players = new Pins<MediaPlayer>()
24
+ /** The host's media table, or undefined on a host without one (`@optional`: the global is absent). */
25
+ const media = (): typeof _creatorMedia | undefined => (typeof _creatorMedia !== "undefined" ? _creatorMedia : undefined)
26
+ let emitterInstalled = false
27
+ const ensureEmitter = (): void => {
28
+ const m = media()
29
+ if (emitterInstalled || !m || typeof m.setEmitter !== "function") return
30
+ emitterInstalled = true
31
+ m.setEmitter((id) => { players.get(id)?._finished() })
32
+ }
33
+
20
34
  abstract class MediaPlayer extends Emitter<MediaEvents> {
21
- protected _id = -1
35
+ /** @internal The player's owned handle (`_h.id` = the host player id; -1 = no host player). */
36
+ _h: Handle = { id: -1 }
22
37
  protected _src = ""
23
38
  private _playing = false
24
39
  private _loop = false
25
40
  private _volume = 1
26
- private _disposed = false
27
-
28
- // Every host-touching operation runs through this so a disposed player can never address a freed
29
- // (and possibly reused) host slot.
30
- private _assertAlive(): void {
31
- if (this._disposed) throw new Error("MediaPlayer has been disposed")
32
- }
33
41
 
34
42
  play(): void { this.playing = true }
35
43
  pause(): void { this.playing = false }
44
+ /** Pause and rewind to the start. */
45
+ stop(): void {
46
+ this.playing = false
47
+ media()?.setTime?.(this._h.id, 0)
48
+ }
36
49
 
37
50
  get playing(): boolean { return this._playing }
38
51
  set playing(value: boolean) {
39
- this._assertAlive()
40
52
  this._playing = value
41
- _creatorUtils.updateMediaPlayerPlaying(this._id, value)
53
+ media()?.setPlaying(this._h.id, value)
54
+ if (value) players.pin(this) // playing: native owns it, the end report must find it
55
+ else players.unpin(this._h.id)
42
56
  }
43
57
 
44
58
  get volume(): number { return this._volume }
45
59
  set volume(value: number) {
46
- this._assertAlive()
47
60
  this._volume = value
48
- _creatorUtils.updateMediaPlayerVolume(this._id, value)
61
+ media()?.setVolume(this._h.id, value)
49
62
  }
50
63
 
51
64
  get loop(): boolean { return this._loop }
52
65
  set loop(value: boolean) {
53
- this._assertAlive()
54
66
  this._loop = value
55
- _creatorUtils.updateMediaPlayerLoop(this._id, value)
67
+ media()?.setLoop(this._h.id, value)
56
68
  }
57
69
 
58
70
  /** Current playback position, in seconds. Settable to seek. */
59
- get time(): number { this._assertAlive(); return _creatorUtils.getMediaPlayerTime(this._id) }
60
- set time(value: number) { this._assertAlive(); _creatorUtils.setMediaPlayerTime(this._id, value) }
71
+ get time(): number { return media()?.time?.(this._h.id) ?? 0 }
72
+ set time(value: number) { media()?.setTime?.(this._h.id, value) }
61
73
 
62
74
  /** Total media length in seconds (0 until metadata has loaded). */
63
- get duration(): number { this._assertAlive(); return _creatorUtils.getMediaPlayerDuration(this._id) }
75
+ get duration(): number { return media()?.duration?.(this._h.id) ?? 0 }
64
76
 
65
- /** True once {@link dispose} has been called; the player is unusable afterwards. */
66
- get disposed(): boolean { return this._disposed }
67
-
68
- /**
69
- * Release the underlying host media resource (decoder, network, buffers) immediately and detach
70
- * all listeners. Idempotent. Any further use of the player throws.
71
- */
72
- dispose(): void {
73
- if (this._disposed) return
74
- this._disposed = true
75
- this._playing = false
76
- this.clearListeners()
77
- _creatorUtils.removeMediaPlayer(this._id)
78
- }
79
-
80
- /** @internal — the host calls this when a play-through finishes. */
81
- protected _finished(): void {
82
- if (this._disposed) return
77
+ /** @internal — the runtime reports a play-through end. */
78
+ _finished(): void {
83
79
  if (this._loop) {
84
80
  this.dispatch("loopReached")
85
81
  } else {
86
82
  this._playing = false
83
+ players.unpin(this._h.id)
87
84
  this.dispatch("completed")
88
85
  }
89
86
  }
87
+
88
+ /** @deprecated There is nothing to dispose — a player the app stops referencing is released by
89
+ * the runtime (a playing one at its end). Kept as `stop()` so old code still silences the player;
90
+ * call `stop()` / `pause()` instead. */
91
+ dispose(): void { this.stop() }
90
92
  }
91
93
 
92
94
  export class AudioPlayer extends MediaPlayer {
93
95
  constructor(src?: string) {
94
96
  super()
95
97
  this._src = src ?? ""
96
- this._id = _creatorUtils.createAudioPlayer(this._src, () => this._finished())
98
+ ensureEmitter()
99
+ this._h = media()?.createAudio(this._src) ?? { id: -1 }
97
100
  }
98
101
  }
99
102
 
@@ -101,11 +104,12 @@ export class VideoPlayer extends MediaPlayer {
101
104
  constructor(src?: string) {
102
105
  super()
103
106
  this._src = src ?? ""
104
- this._id = _creatorUtils.createVideoPlayer(this._src, () => this._finished())
107
+ ensureEmitter()
108
+ this._h = media()?.createVideo(this._src) ?? { id: -1 }
105
109
  }
106
110
 
107
- /** A 3D Texture that samples this video (e.g. for a Material.video()). */
111
+ /** A 3D Texture that samples this video (e.g. for a Material.video()). Owned by the player. */
108
112
  get texture(): Texture {
109
- return new Texture(0, 0, _creator.createMediaPlayerTexture(this._id))
113
+ return new Texture(0, 0, _creator.createMediaPlayerTexture(this._h.id))
110
114
  }
111
115
  }
@@ -1,15 +1,19 @@
1
1
  // Small platform globals that are real (not engine-specific). Grown in Phase 1.
2
2
 
3
+ import type { ColorInput } from "../core/color"
4
+
3
5
  /** Show a transient toast notification. */
4
- export const toast = (msg: string): void => _creatorUI.toast(msg)
6
+ export const toast = (msg: string): void => _creatorTree.toast(msg, 2000)
5
7
 
6
8
  /** Open `url` in the system browser / external handler (fire-and-forget, like `share`). Silent
7
9
  * no-op on hosts without one (headless). */
8
- export const openURL = (url: string): void => _creatorUtils.openUrl?.(url)
10
+ export const openURL = (url: string): void => _creatorApp.openUrl?.(url)
9
11
 
10
12
  export type SvgSourceValue = {
11
13
  readonly svg: string
12
- tintColor: string | null
14
+ /** The source-level tint (a mask recolor under the `tintColor` style, which wins): any color, or
15
+ * a theme `var(--…)`. */
16
+ tintColor: ColorInput | null
13
17
  }
14
18
 
15
19
  /** Wrap raw SVG XML so it can be used as an image source. */
@@ -1,5 +1,5 @@
1
- // WebSocket over the host bridge. Same event surface as the browser's (open/message/close/error)
2
- // via the shared Emitter. Ported from worker/src/WebSocket.ts.
1
+ // WebSocket over the host's `_creatorSocket` bridge. Same event surface as the browser's
2
+ // (open/message/close/error) via the shared Emitter.
3
3
 
4
4
  import { Emitter } from "../core/events"
5
5
 
@@ -13,18 +13,19 @@ type SocketEvents = {
13
13
  export class WebSocket extends Emitter<SocketEvents> {
14
14
  private _id: number | null = null
15
15
 
16
- constructor(url: string, headers?: Record<string, string>) {
16
+ constructor(url: string) {
17
17
  super()
18
- this._id = _creatorUtils.websocketOpen(url, (channel, data) => this._onMessage(channel, data), headers)
18
+ this._id = _creatorSocket.open(url, (channel, data) => this._onMessage(channel, data))
19
19
  }
20
20
 
21
- send(message: string | ArrayBuffer): void {
22
- if (this._id !== null) _creatorUtils.websocketSend(this._id, message)
21
+ /** Send a text frame. */
22
+ send(message: string): void {
23
+ if (this._id !== null) _creatorSocket.send(this._id, message)
23
24
  }
24
25
 
25
26
  close(): void {
26
27
  if (this._id !== null) {
27
- _creatorUtils.websocketClose(this._id)
28
+ _creatorSocket.close(this._id)
28
29
  this._id = null
29
30
  }
30
31
  }
@@ -68,7 +68,6 @@ export const __rpc = (serverUrl: string, id: string) => {
68
68
  }
69
69
  let body: any
70
70
  try { body = res.json() } catch { body = null }
71
- res.dispose()
72
71
  if (body && typeof body === "object" && "session" in body) setSession(serverUrl, body.session ?? null)
73
72
  if (body && body.ok === true) return body.result
74
73
  if (body && body.ok === false) throw new RpcError(body.status ?? res.status, body.message ?? "")
@@ -132,8 +131,7 @@ class ChannelSocket {
132
131
  if (this.ws || this.timer !== null) return
133
132
  this.closedByUs = false
134
133
  const url = this.serverUrl.replace(/^http/, "ws") + "/ws"
135
- const session = getSession(this.serverUrl)
136
- const ws = new WebSocket(url, session ? { authorization: `Bearer ${session}` } : undefined)
134
+ const ws = new WebSocket(url)
137
135
  this.ws = ws
138
136
  ws.addEventListener("open", () => {
139
137
  this.open = true
@@ -1,7 +1,7 @@
1
1
  // A host-registered headless service (geolocation, bluetooth, contacts, …) — the UI-less sibling
2
2
  // of NativeView (docs/service-channel-plan.md). The host registers a *capability*
3
3
  // (`registerService("geolocation") { params, channel -> ... }`); the SDK talks to it over the
4
- // shared JSON call/event protocol (runtime/channel.ts) via `_creatorUtils.service*`.
4
+ // shared call / event protocol (runtime/channel.ts) via `_creatorService`.
5
5
  //
6
6
  // Not a public global itself: apps meet services through typed plugin wrappers (Geolocation —
7
7
  // src/plugins/) or the raw `Service(name)` global (src/plugins/service.ts), both thin layers
@@ -26,18 +26,22 @@ export class ServiceClient {
26
26
 
27
27
  /** Whether this host registered a `registerService` factory under `name`. */
28
28
  static isSupported(name: string): boolean {
29
- return typeof _creatorUtils !== "undefined" && typeof _creatorUtils.isServiceSupported === "function"
30
- && _creatorUtils.isServiceSupported(name)
29
+ return typeof _creatorService !== "undefined" && _creatorService.supported(name)
30
+ }
31
+
32
+ /** The contract version of the host's service `name`: 0 when it has none, 1 for one written by hand. */
33
+ static version(name: string): number {
34
+ return typeof _creatorService !== "undefined" ? _creatorService.version(name) : 0
31
35
  }
32
36
 
33
37
  /** @internal open (or reuse) the host session; throws when the host has no channel/factory. */
34
38
  private _ensureOpen(): number {
35
39
  if (this._serviceId !== null) return this._serviceId
36
- if (typeof _creatorUtils === "undefined" || typeof _creatorUtils.serviceOpen !== "function") {
37
- throw new Error(`Service("${this.serviceName}") is not supported on this host (requires _creatorUtils.serviceOpen)`)
40
+ if (typeof _creatorService === "undefined") {
41
+ throw new Error(`Service("${this.serviceName}") is not supported on this host (no service channel)`)
38
42
  }
39
- const id = _creatorUtils.serviceOpen(this.serviceName, JSON.stringify(this.params ?? {}),
40
- (event: string, dataJson?: string) => _channelEmit(this._listeners, event, dataJson))
43
+ const id = _creatorService.open(this.serviceName, JSON.stringify(this.params ?? {}),
44
+ (event: string, data?: unknown) => _channelEmit(this._listeners, event, data))
41
45
  if (id < 0) {
42
46
  throw new Error(`Service("${this.serviceName}") is not registered on this host`)
43
47
  }
@@ -46,8 +50,11 @@ export class ServiceClient {
46
50
  }
47
51
 
48
52
  /** Invoke a method on the service (`geo.call("getCurrent", options)`). Args and the result are
49
- * JSON-serialized. Rejects if the host has no such service/method. */
50
- call(method: string, ...args: any[]): Promise<any> {
53
+ * plain data (and Uint8Array). Rejects if the host has no such service/method. */
54
+ call(method: string, ...args: any[]): Promise<any> { return this._channelSend(method, args) }
55
+
56
+ /** @internal One call over the channel (runtime/channel.ts). */
57
+ _channelSend(method: string, args: any[]): Promise<any> {
51
58
  let id: number
52
59
  try {
53
60
  id = this._ensureOpen()
@@ -55,8 +62,8 @@ export class ServiceClient {
55
62
  return Promise.reject(e)
56
63
  }
57
64
  return _channelCall(
58
- typeof _creatorUtils !== "undefined" ? _creatorUtils : undefined, "serviceCall",
59
- `Service("${this.serviceName}") is not supported on this host (requires _creatorUtils.serviceCall)`,
65
+ typeof _creatorService !== "undefined" ? _creatorService : undefined, "call",
66
+ `Service("${this.serviceName}") is not supported on this host (no service channel)`,
60
67
  id, method, args)
61
68
  }
62
69
 
@@ -76,8 +83,6 @@ export class ServiceClient {
76
83
  if (this._serviceId === null) return
77
84
  const id = this._serviceId
78
85
  this._serviceId = null
79
- if (typeof _creatorUtils !== "undefined" && typeof _creatorUtils.serviceClose === "function") {
80
- _creatorUtils.serviceClose(id)
81
- }
86
+ if (typeof _creatorService !== "undefined") _creatorService.close(id)
82
87
  }
83
88
  }
@@ -1,9 +1,10 @@
1
- // Share a media File (image/video) via the OS share sheet. Wraps `_creatorUtils.shareMedia`, which
1
+ // Share a media File (image/video) via the OS share sheet. Wraps `_creatorDevice.share`, which
2
2
  // takes the host buffer id behind the File. On iOS the sheet also lets the user save to Files/Photos;
3
- // on web it saves (downloads) the file. `text` is an optional caption/message.
3
+ // on web it saves (downloads) the file. `text` is an optional caption/message. A silent no-op on
4
+ // hosts without a share slot (headless).
4
5
 
5
6
  import type { File, FetchResponse } from "./fetch"
6
7
 
7
8
  export function share(media: File | FetchResponse, text?: string): void {
8
- _creatorUtils.shareMedia((media as any)._id, text)
9
+ _creatorDevice.share?.((media as any)._h.id, text)
9
10
  }
@@ -1,13 +1,15 @@
1
- // Key/value persistence backed by the host. Same shape as the web localStorage subset projects use.
1
+ // Key/value persistence backed by the host (`_creatorStorage`). Same shape as the web localStorage
2
+ // subset projects use.
3
+
2
4
 
3
5
  export const localStorage = {
4
6
  getItem(key: string): string | null {
5
- return _creatorUtils.localStorageGetValue(key)
7
+ return _creatorStorage.get(key)
6
8
  },
7
9
  setItem(key: string, value: string): void {
8
- _creatorUtils.localStorageSetValue(key, value)
10
+ _creatorStorage.set(key, value)
9
11
  },
10
12
  removeItem(key: string): void {
11
- _creatorUtils.localStorageRemoveValue(key)
13
+ _creatorStorage.remove(key)
12
14
  },
13
15
  }
@@ -74,3 +74,35 @@ export class LongPressEvent<T = any> {
74
74
  this._trackHandler = handler
75
75
  }
76
76
  }
77
+
78
+ /** What a hover `track()` takes: the cursor's moves inside the element (only delivered when
79
+ * `onMove` is present — otherwise the host never crosses into JS for them), `onEnd` when it
80
+ * leaves (with the last position), `onCancel` when the element went away underneath it. No
81
+ * `claim`: a hover competes with nothing. */
82
+ export interface HoverTrackHandler {
83
+ onMove?(pos: { clientX: number, clientY: number, deltaX: number, deltaY: number }): void
84
+ onEnd?(pos: { clientX: number, clientY: number, deltaX: number, deltaY: number }): void
85
+ onCancel?(): void
86
+ }
87
+
88
+ /** Delivered to a `UIButton` `onMouseEnter` handler when a mouse cursor (no button) enters the
89
+ * button. There is no separate leave event: `ev.track({ onEnd })` follows this hover to its end,
90
+ * `onMove` follows the cursor inside — the visual state itself is the reserved `$hovered` class.
91
+ * Touch never produces one. */
92
+ export class MouseEnterEvent<T = any> {
93
+ readonly type = "mouseenter"
94
+ readonly clientX: number
95
+ readonly clientY: number
96
+ target!: T
97
+ /** @internal — read by the dispatch system after the handler runs. */
98
+ _trackHandler?: HoverTrackHandler
99
+ constructor(clientX: number, clientY: number) {
100
+ this.clientX = clientX
101
+ this.clientY = clientY
102
+ }
103
+
104
+ /** Follow this hover: moves inside the element, its end (the cursor left), its cancel. */
105
+ track(handler: HoverTrackHandler): void {
106
+ this._trackHandler = handler
107
+ }
108
+ }