lecodes-cli 0.18.2 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/dist/index.js +1866 -301
  2. package/package.json +14 -12
  3. package/runtime/scene-harness.json +1 -1
  4. package/runtime/sdk-types.json +1 -1
  5. package/src/api.ts +302 -0
  6. package/src/browserAuth.ts +87 -0
  7. package/src/cmgenTool.ts +104 -0
  8. package/src/commands/app.ts +891 -0
  9. package/src/commands/appAndroid.ts +603 -0
  10. package/src/commands/appDesktop.ts +326 -0
  11. package/src/commands/appDesktopMac.ts +470 -0
  12. package/src/commands/appIcon.ts +187 -0
  13. package/src/commands/appShared.ts +448 -0
  14. package/src/commands/appTemplates.ts +883 -0
  15. package/src/commands/appTemplatesAndroid.ts +599 -0
  16. package/src/commands/appTemplatesGradlew.ts +9 -0
  17. package/src/commands/assets.ts +28 -0
  18. package/src/commands/clone.ts +60 -0
  19. package/src/commands/compile.ts +253 -0
  20. package/src/commands/create.ts +59 -0
  21. package/src/commands/design.ts +863 -0
  22. package/src/commands/designTemplates.ts +11 -0
  23. package/src/commands/desktop.ts +122 -0
  24. package/src/commands/dev.ts +214 -0
  25. package/src/commands/diff.ts +59 -0
  26. package/src/commands/init.ts +191 -0
  27. package/src/commands/install.ts +198 -0
  28. package/src/commands/lightmap.ts +290 -0
  29. package/src/commands/link.ts +147 -0
  30. package/src/commands/login.ts +24 -0
  31. package/src/commands/navmesh.ts +225 -0
  32. package/src/commands/pn.ts +305 -0
  33. package/src/commands/projectTemplates.ts +129 -0
  34. package/src/commands/pull.ts +109 -0
  35. package/src/commands/push.ts +173 -0
  36. package/src/commands/render.ts +414 -0
  37. package/src/commands/scene.ts +148 -0
  38. package/src/commands/shaders.ts +157 -0
  39. package/src/commands/status.ts +29 -0
  40. package/src/commands/test.ts +370 -0
  41. package/src/commands/thumbs.ts +178 -0
  42. package/src/commands/types.ts +87 -0
  43. package/src/commands/update.ts +190 -0
  44. package/src/compile/assetIcons.ts +214 -0
  45. package/src/compile/collect.ts +46 -0
  46. package/src/compile/collectLocal.ts +37 -0
  47. package/src/compile/designCompile.ts +104 -0
  48. package/src/compile/fonts.ts +156 -0
  49. package/src/compile/headlessBundle.ts +129 -0
  50. package/src/compile/nativeStack.ts +21 -0
  51. package/src/compile/projectCompile.ts +109 -0
  52. package/src/compile/sceneCompile.ts +113 -0
  53. package/src/compile/screenEntry.ts +127 -0
  54. package/src/compile/shaders.ts +245 -0
  55. package/src/config.ts +42 -0
  56. package/src/designMeta.ts +35 -0
  57. package/src/desktopRenderer.ts +532 -0
  58. package/src/desktopScript.ts +276 -0
  59. package/src/dev/clientTemplates.ts +160 -0
  60. package/src/dev/devServer.ts +292 -0
  61. package/src/dev/wsServer.ts +144 -0
  62. package/src/distRoot.ts +20 -0
  63. package/src/ignore.ts +163 -0
  64. package/src/index.ts +491 -0
  65. package/src/lecodes-3d-editor.d.ts +41 -0
  66. package/src/lecodes-assets.d.ts +7 -0
  67. package/src/lecodes-design.d.ts +191 -0
  68. package/src/lecodes-renderer.d.ts +131 -0
  69. package/src/localFiles.ts +144 -0
  70. package/src/manifest.ts +46 -0
  71. package/src/matcTool.ts +137 -0
  72. package/src/peers.ts +40 -0
  73. package/src/project.ts +61 -0
  74. package/src/projectEnv.ts +94 -0
  75. package/src/qrcode-terminal.d.ts +9 -0
  76. package/src/releases.ts +125 -0
  77. package/src/serverDiff.ts +78 -0
  78. package/src/textDiff.ts +103 -0
  79. package/src/types.ts +0 -0
  80. package/src/util.ts +146 -0
  81. package/runtime/sdk/animate/animate.ts +0 -238
  82. package/runtime/sdk/animate/bezier.ts +0 -138
  83. package/runtime/sdk/animate/easings.ts +0 -126
  84. package/runtime/sdk/canvas/Canvas.ts +0 -305
  85. package/runtime/sdk/core/Aspect.ts +0 -512
  86. package/runtime/sdk/core/InspectorUI.ts +0 -212
  87. package/runtime/sdk/core/color.ts +0 -66
  88. package/runtime/sdk/core/compWrite.ts +0 -42
  89. package/runtime/sdk/core/events.ts +0 -38
  90. package/runtime/sdk/core/fields.ts +0 -120
  91. package/runtime/sdk/core/registry.ts +0 -23
  92. package/runtime/sdk/core/signals.ts +0 -277
  93. package/runtime/sdk/core/time.ts +0 -81
  94. package/runtime/sdk/g2/Camera2D.ts +0 -40
  95. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  96. package/runtime/sdk/g2/Node2D.ts +0 -267
  97. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  98. package/runtime/sdk/g2/Physics2D.ts +0 -346
  99. package/runtime/sdk/g2/Scene2D.ts +0 -209
  100. package/runtime/sdk/g2/Shape2D.ts +0 -259
  101. package/runtime/sdk/g2/Sprite.ts +0 -89
  102. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  103. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  104. package/runtime/sdk/g2/Texture2D.ts +0 -47
  105. package/runtime/sdk/g2/Tilemap.ts +0 -41
  106. package/runtime/sdk/g2/Tileset.ts +0 -71
  107. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  108. package/runtime/sdk/g2/autotile.ts +0 -433
  109. package/runtime/sdk/g2/cells.ts +0 -91
  110. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  111. package/runtime/sdk/g2/groups2d.ts +0 -106
  112. package/runtime/sdk/g2/loop.ts +0 -50
  113. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  114. package/runtime/sdk/g2/touch.ts +0 -83
  115. package/runtime/sdk/gl/Camera.ts +0 -160
  116. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  117. package/runtime/sdk/gl/CharacterController.ts +0 -238
  118. package/runtime/sdk/gl/Gearbox.ts +0 -212
  119. package/runtime/sdk/gl/Geometry.ts +0 -279
  120. package/runtime/sdk/gl/IK.ts +0 -193
  121. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  122. package/runtime/sdk/gl/Light.ts +0 -99
  123. package/runtime/sdk/gl/Lightmap.ts +0 -179
  124. package/runtime/sdk/gl/Material.ts +0 -245
  125. package/runtime/sdk/gl/Mesh.ts +0 -83
  126. package/runtime/sdk/gl/Model.ts +0 -64
  127. package/runtime/sdk/gl/Node.ts +0 -350
  128. package/runtime/sdk/gl/Noise.ts +0 -30
  129. package/runtime/sdk/gl/Particles.ts +0 -676
  130. package/runtime/sdk/gl/Physics.ts +0 -222
  131. package/runtime/sdk/gl/Plane.ts +0 -53
  132. package/runtime/sdk/gl/Ray.ts +0 -16
  133. package/runtime/sdk/gl/Scene.ts +0 -479
  134. package/runtime/sdk/gl/Shape.ts +0 -377
  135. package/runtime/sdk/gl/Texture.ts +0 -46
  136. package/runtime/sdk/gl/Trigger.ts +0 -45
  137. package/runtime/sdk/gl/Vehicle.ts +0 -473
  138. package/runtime/sdk/gl/Wheel.ts +0 -240
  139. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  140. package/runtime/sdk/gl/animation/Animator.ts +0 -87
  141. package/runtime/sdk/gl/animation/Layer.ts +0 -29
  142. package/runtime/sdk/gl/animation/Loop.ts +0 -25
  143. package/runtime/sdk/gl/animation/Playback.ts +0 -43
  144. package/runtime/sdk/gl/animation/core.ts +0 -294
  145. package/runtime/sdk/gl/controls.ts +0 -95
  146. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  147. package/runtime/sdk/gl/scenarios.ts +0 -291
  148. package/runtime/sdk/gl/state.ts +0 -6
  149. package/runtime/sdk/gl/touch.ts +0 -68
  150. package/runtime/sdk/inject.ts +0 -186
  151. package/runtime/sdk/math/Mathf.ts +0 -118
  152. package/runtime/sdk/math/mat4.ts +0 -278
  153. package/runtime/sdk/math/quat.ts +0 -232
  154. package/runtime/sdk/math/vec.ts +0 -255
  155. package/runtime/sdk/plugins/camera.ts +0 -81
  156. package/runtime/sdk/plugins/geolocation.ts +0 -123
  157. package/runtime/sdk/plugins/oauth.ts +0 -61
  158. package/runtime/sdk/plugins/permission.ts +0 -7
  159. package/runtime/sdk/plugins/push.ts +0 -132
  160. package/runtime/sdk/plugins/qr.ts +0 -73
  161. package/runtime/sdk/plugins/service.ts +0 -47
  162. package/runtime/sdk/runtime/app.ts +0 -101
  163. package/runtime/sdk/runtime/appEvents.ts +0 -54
  164. package/runtime/sdk/runtime/channel.ts +0 -61
  165. package/runtime/sdk/runtime/clipboard.ts +0 -20
  166. package/runtime/sdk/runtime/datetime.ts +0 -329
  167. package/runtime/sdk/runtime/device.ts +0 -293
  168. package/runtime/sdk/runtime/fetch.ts +0 -77
  169. package/runtime/sdk/runtime/files.ts +0 -23
  170. package/runtime/sdk/runtime/input.ts +0 -175
  171. package/runtime/sdk/runtime/media.ts +0 -111
  172. package/runtime/sdk/runtime/misc.ts +0 -16
  173. package/runtime/sdk/runtime/net.ts +0 -36
  174. package/runtime/sdk/runtime/rpc.ts +0 -218
  175. package/runtime/sdk/runtime/service.ts +0 -83
  176. package/runtime/sdk/runtime/share.ts +0 -9
  177. package/runtime/sdk/runtime/storage.ts +0 -13
  178. package/runtime/sdk/runtime/touch.ts +0 -76
  179. package/runtime/sdk/scene/defineScene.ts +0 -1227
  180. package/runtime/sdk/scene/editorPlugins.ts +0 -92
  181. package/runtime/sdk/scene/gizmos.ts +0 -148
  182. package/runtime/sdk/scene/grammar.ts +0 -120
  183. package/runtime/sdk/scene/material.ts +0 -188
  184. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  185. package/runtime/sdk/server/auth/global.ts +0 -80
  186. package/runtime/sdk/server/auth/host.ts +0 -318
  187. package/runtime/sdk/server/auth/models.ts +0 -83
  188. package/runtime/sdk/server/auth/types.ts +0 -50
  189. package/runtime/sdk/server/channel.ts +0 -56
  190. package/runtime/sdk/server/context.ts +0 -36
  191. package/runtime/sdk/server/db/defineDb.ts +0 -237
  192. package/runtime/sdk/server/db/fields.ts +0 -132
  193. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  194. package/runtime/sdk/server/db/index.ts +0 -7
  195. package/runtime/sdk/server/db/marci/query.ts +0 -412
  196. package/runtime/sdk/server/db/types.ts +0 -202
  197. package/runtime/sdk/server/errors.ts +0 -12
  198. package/runtime/sdk/server/host.ts +0 -74
  199. package/runtime/sdk/server/inject.ts +0 -13
  200. package/runtime/sdk/server/runtime.ts +0 -133
  201. package/runtime/sdk/server/validate.ts +0 -87
  202. package/runtime/sdk/ui/NativeView.ts +0 -142
  203. package/runtime/sdk/ui/UI.ts +0 -39
  204. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  205. package/runtime/sdk/ui/UIButton.ts +0 -101
  206. package/runtime/sdk/ui/UIContainer.ts +0 -60
  207. package/runtime/sdk/ui/UIImage.ts +0 -83
  208. package/runtime/sdk/ui/UIInput.ts +0 -185
  209. package/runtime/sdk/ui/UIModal.ts +0 -139
  210. package/runtime/sdk/ui/UINode.ts +0 -826
  211. package/runtime/sdk/ui/UIPager.ts +0 -362
  212. package/runtime/sdk/ui/UIPopover.ts +0 -100
  213. package/runtime/sdk/ui/UIScreen.ts +0 -123
  214. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  215. package/runtime/sdk/ui/UISpacer.ts +0 -14
  216. package/runtime/sdk/ui/UITabs.ts +0 -236
  217. package/runtime/sdk/ui/UIText.ts +0 -51
  218. package/runtime/sdk/ui/UIVideo.ts +0 -88
  219. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  220. package/runtime/sdk/ui/UIWidget.ts +0 -127
  221. package/runtime/sdk/ui/fonts.ts +0 -13
  222. package/runtime/sdk/ui/presentable.ts +0 -117
  223. package/runtime/sdk/ui/router.ts +0 -132
  224. package/runtime/sdk/ui/theme.ts +0 -84
@@ -1,73 +0,0 @@
1
- // QRScanner — the host's camera QR scanner as a Presentable. A typed wrapper over the
2
- // `NativeView("qrScanner")` capability (docs/navigation-presentable-plan.md): open it fullscreen
3
- // (`await scanner.open()`), push it (`Router.push(scanner)`), or embed it among a screen's
4
- // children as a live preview node — everything a NativeView can do, plus a typed `onScan`.
5
- //
6
- // Host-OPTIONAL, like every plugin: it exists where the host registers a "qrScanner" view
7
- // (Android; web can via `_creatorUI._registerView`) — gate on `QRScanner.isSupported`. Opening
8
- // prepares first (camera permission) while the current destination stays visible — the same
9
- // prepare-then-present contract as ARScene.
10
- //
11
- // The pre-Presentable `_creatorUtils.openScanner/closeScanner` bridges are gone from the ABI
12
- // (2026-07-15): hosts keep their implementations only to serve previously compiled bundles.
13
-
14
- import { _navEpoch, type PresentOptions } from "../ui/presentable"
15
- import { NativeView, NativeViewElement } from "../ui/NativeView"
16
- import { _requestCameraPermission } from "./permission"
17
-
18
- export interface QRScanner extends NativeView {
19
- /** Fires per decoded camera frame: the decoded string, or `null` for a frame with no readable
20
- * code (expect those repeatedly while the user aims). The same code can be reported more than
21
- * once — `close()` or debounce once you have what you need. */
22
- onScan(callback: (data: string | null) => void): this
23
- /** Request camera permission, then present the scanner. The current destination (and its
24
- * loading state) stays visible until the scanner is ready; rejects if the camera is denied,
25
- * the host can't scan, or another navigation superseded this one. */
26
- open(options?: PresentOptions): Promise<void>
27
- }
28
-
29
- class QRScannerElement extends NativeViewElement {
30
- constructor() {
31
- super("qrScanner", {})
32
- }
33
-
34
- onScan(callback: (data: string | null) => void): this {
35
- return this.on("scan", (payload?: { data?: string | null }) => callback(payload?.data ?? null))
36
- }
37
-
38
- /** @internal prepare-then-present: permission before the swap (see Presentable._prepare). */
39
- async _prepare(): Promise<void> {
40
- const epoch = _navEpoch()
41
- await _requestCameraPermission()
42
- if (_navEpoch() !== epoch) {
43
- throw new Error("QRScanner.open() superseded: another destination was opened while preparing")
44
- }
45
- }
46
-
47
- override async open(options?: PresentOptions): Promise<void> {
48
- // Fail before prompting for the camera — a permission dialog for a scanner that can't exist
49
- // would be worse than the error.
50
- if (!NativeView.isSupported("qrScanner")) {
51
- throw new Error("QRScanner isn't available on this host — it needs a registered \"qrScanner\" view.")
52
- }
53
- await this._prepare()
54
- super.open(options)
55
- }
56
- }
57
-
58
- /**
59
- * Create a QR scanner view. `QRScanner.isSupported` reports whether this host can scan at all —
60
- * check it before offering the feature.
61
- */
62
- // PURE IIFE so an app that never scans tree-shakes the whole plugin away (see NativeView).
63
- export const QRScanner: {
64
- (): QRScanner
65
- /** Whether this host registered a "qrScanner" view. */
66
- readonly isSupported: boolean
67
- } = /*#__PURE__*/ (() => {
68
- const factory = (): QRScanner => new QRScannerElement() as unknown as QRScanner
69
- Object.defineProperty(factory, "isSupported", {
70
- get: (): boolean => NativeView.isSupported("qrScanner"),
71
- })
72
- return factory as any
73
- })()
@@ -1,47 +0,0 @@
1
- // Raw access to host-registered headless services — the escape hatch for services that
2
- // have no typed wrapper (yet). First-party services ship wrapped (Geolocation); app-local
3
- // native code (an `engine.registerService` in the app shell) and third-party plugins are
4
- // reachable through this global immediately, wrapper or not. Thin sugar over the internal
5
- // ServiceClient (src/runtime/service.ts) — same JSON call/event channel, same lazy session.
6
-
7
- import { ServiceClient } from "../runtime/service"
8
-
9
- export interface Service {
10
- /** Invoke a method on the service (`battery.call("level")`). Arguments and the result are
11
- * JSON-serialized; rejects when the host has no such service or the method reports an
12
- * error. The host session opens lazily on the first call. */
13
- call(method: string, ...args: any[]): Promise<any>
14
- /** Subscribe to an event the service emits while a session is open
15
- * (`battery.on("change", data => …)`). Payloads are JSON-decoded. */
16
- on(event: string, callback: (data?: any) => void): this
17
- /** Remove a previously added listener. */
18
- off(event: string, callback: (data?: any) => void): this
19
- /** Close the host session (stops whatever the service was doing). Listeners stay
20
- * registered — a later `call()` opens a fresh session that delivers to them again. */
21
- close(): void
22
- }
23
-
24
- /**
25
- * A host-registered headless service — the UI-less sibling of `NativeView`. Use it for
26
- * services without a typed wrapper: native code registered in your app shell
27
- * (`engine.registerService("app.battery") { … }`) or a third-party plugin's channel.
28
- * **Host-optional** — gate on `Service.isSupported` (the web editor registers none of them).
29
- *
30
- * ```ts
31
- * if (Service.isSupported("app.battery")) {
32
- * const battery = Service("app.battery")
33
- * battery.on("change", data => { level.text = data.level + "%" })
34
- * const { level } = await battery.call("level")
35
- * }
36
- * ```
37
- */
38
- // PURE IIFE so an app that never touches raw services tree-shakes this away.
39
- export const Service: {
40
- (name: string, params?: any): Service
41
- /** Whether this host registered a service under `name`. */
42
- isSupported(name: string): boolean
43
- } = /*#__PURE__*/ (() => {
44
- const factory = (name: string, params: any = {}): Service => new ServiceClient(name, params) as unknown as Service
45
- factory.isSupported = (name: string): boolean => ServiceClient.isSupported(name)
46
- return factory as any
47
- })()
@@ -1,101 +0,0 @@
1
- // The app's own lifecycle + how it was opened (docs/app-lifecycle-plan.md). Events are semantic —
2
- // "pause"/"resume", never Activity/Scene vocabulary — and the current values are synchronous pulls
3
- // so render-loop code can read them without subscribing.
4
-
5
- import { appEventsOn, appEventsOff, keyboardEventsOn, keyboardEventsOff } from "./appEvents"
6
-
7
- export type AppState = "active" | "background"
8
-
9
- /** `"landscape"` — either landscape direction (sensor landscape), `"portrait"` — upright only,
10
- * `"auto"` — released: the device's own rotation rules apply. */
11
- export type OrientationLock = "auto" | "portrait" | "landscape"
12
-
13
- type AppEventMap = {
14
- /** The app left the foreground (home button, tab hidden, another app on top). Delivered before
15
- * the host halts the frame loop — the last chance to persist state / pause work. */
16
- pause: () => void
17
- /** The app returned to the foreground; frames are running again. */
18
- resume: () => void
19
- /** A link arrived while the app was running (warm deep link). `app.launchUrl` is already
20
- * updated to the same value when this fires. */
21
- url: (url: string) => void
22
- /** The on-screen keyboard changed. `height` mirrors `app.keyboardHeight` (the raw overlap, see
23
- * there); `duration` is the platform's keyboard animation duration in ms (0 where none) — pass
24
- * it to `animateTo` to move a composer in sync with the keyboard. */
25
- keyboard: (height: number, duration: number) => void
26
- }
27
-
28
- export const app = {
29
- /** Current lifecycle state. `"background"` while the app is not the foreground app / the tab is
30
- * hidden. `"active"` on hosts that don't track it. */
31
- get state(): AppState {
32
- return _creatorUtils.appState?.() === "background" ? "background" : "active"
33
- },
34
- /** The URL the app was (most recently) opened with — the deep link — or `null` for a plain
35
- * launch. Warm links update it and fire the `"url"` event. */
36
- get launchUrl(): string | null {
37
- return _creatorUtils.getLaunchUrl?.() ?? null
38
- },
39
- /** On-screen keyboard height in logical px currently overlapping the app viewport, `0` when
40
- * hidden. This is the RAW overlap, independent of the focused input's `keyboardShrink` policy —
41
- * with the default shrink the layout already avoids the keyboard; the main consumer is overlay
42
- * mode (`keyboardShrink: false`), e.g. a chat composer's `pb`. `0` on hosts without an
43
- * on-screen keyboard. */
44
- get keyboardHeight(): number {
45
- return _creatorUtils.keyboardHeight?.() ?? 0
46
- },
47
- /** Lock the screen orientation — `"landscape"` for a horizontal game (the device rotates to
48
- * landscape right away and stays there whichever way it is held), `"portrait"` for upright
49
- * only, `"auto"` to release. Call it once at startup; the lock is per app run (it resets
50
- * when the app quits or restarts), so there is nothing to undo. The rotation arrives as an
51
- * ordinary resize — layouts reflow, a scene's viewport follows. Silent no-op on hosts
52
- * without a rotatable screen (desktop, headless, web). */
53
- setOrientation(mode: OrientationLock): void {
54
- _creatorUtils.setOrientation?.(mode)
55
- },
56
- /** Re-run this app's bundle in a fresh world — the platform's `location.reload()`. Pass `url`
57
- * to reboot with different launch arguments (the new run reads them via `app.launchUrl`),
58
- * `null` to reboot with a cleared one, or omit it to replay the current one. No-op on hosts
59
- * without world control (headless). */
60
- restart(url?: string | null): void {
61
- _creatorUtils.restart?.(url)
62
- },
63
- /** Leave this app. Inside the LeCodes viewer that returns to the launcher (the LeCodes home
64
- * screen); a standalone-built app leaves to the platform instead — Android backgrounds it,
65
- * iOS ignores the call (the platform forbids programmatic exit). */
66
- quit(): void {
67
- _creatorUtils.quit?.()
68
- },
69
- /** Global back-press fallback (Android back button/gesture — iOS has no system back). Fires
70
- * only when nothing else consumed the press: no open widget/modal claimed it, the current
71
- * screen has no `onBackPressed`, every pager is at its tab root and the router is at its
72
- * stack root. The place for app-wide policy — e.g. double-press-to-quit:
73
- *
74
- * ```ts
75
- * let armedAt = 0
76
- * app.onBackPressed(() => {
77
- * if (Date.now() - armedAt < 2000) { app.quit(); return }
78
- * armedAt = Date.now()
79
- * toast("Press back again to exit")
80
- * })
81
- * ```
82
- *
83
- * Replaces the previous handler; pass `null` to remove. */
84
- onBackPressed(callback: (() => void) | null): void {
85
- _creatorUI._backButtonCallback = callback ?? undefined
86
- },
87
- addEventListener<E extends keyof AppEventMap>(event: E, callback: AppEventMap[E]): void {
88
- if (event === "keyboard") {
89
- keyboardEventsOn(callback as (height: number, duration: number) => void)
90
- return
91
- }
92
- appEventsOn(event, callback as (data?: string) => void)
93
- },
94
- removeEventListener<E extends keyof AppEventMap>(event: E, callback: AppEventMap[E]): void {
95
- if (event === "keyboard") {
96
- keyboardEventsOff(callback as (height: number, duration: number) => void)
97
- return
98
- }
99
- appEventsOff(event, callback as (data?: string) => void)
100
- },
101
- }
@@ -1,54 +0,0 @@
1
- // The shared app-event dispatcher. The host exposes ONE registerAppEvent slot per JS world
2
- // (docs/app-lifecycle-plan.md), so every SDK consumer (the `app` global's pause/resume/url, the
3
- // `device` global's online/offline) subscribes here and this module registers with the host once,
4
- // lazily on the first subscription — no top-level side effect, tree-shaking drops it when unused.
5
-
6
- type AppEventCallback = (data?: string) => void
7
-
8
- const listeners: Record<string, AppEventCallback[]> = {}
9
- let registered = false
10
-
11
- const dispatch = (event: string, data?: string): void => {
12
- const arr = listeners[event]
13
- if (arr) for (const cb of arr.slice()) cb(data)
14
- }
15
-
16
- export const appEventsOn = (event: string, cb: AppEventCallback): void => {
17
- ;(listeners[event] ??= []).push(cb)
18
- if (!registered) {
19
- registered = true
20
- _creatorUtils.registerAppEvent?.(dispatch)
21
- }
22
- }
23
-
24
- export const appEventsOff = (event: string, cb: AppEventCallback): void => {
25
- const arr = listeners[event]
26
- if (!arr) return
27
- const i = arr.indexOf(cb)
28
- if (i >= 0) arr.splice(i, 1)
29
- }
30
-
31
- // The keyboard channel is a separate host slot (registerKeyboardEvent) — the registerAppEvent
32
- // string channel can't carry the (height, duration) number pair. Same lazy single-slot pattern.
33
-
34
- type KeyboardCallback = (height: number, duration: number) => void
35
-
36
- const keyboardListeners: KeyboardCallback[] = []
37
- let keyboardRegistered = false
38
-
39
- const dispatchKeyboard = (height: number, duration: number): void => {
40
- for (const cb of keyboardListeners.slice()) cb(height, duration)
41
- }
42
-
43
- export const keyboardEventsOn = (cb: KeyboardCallback): void => {
44
- keyboardListeners.push(cb)
45
- if (!keyboardRegistered) {
46
- keyboardRegistered = true
47
- _creatorUtils.registerKeyboardEvent?.(dispatchKeyboard)
48
- }
49
- }
50
-
51
- export const keyboardEventsOff = (cb: KeyboardCallback): void => {
52
- const i = keyboardListeners.indexOf(cb)
53
- if (i >= 0) keyboardListeners.splice(i, 1)
54
- }
@@ -1,61 +0,0 @@
1
- // The JSON wire protocol shared by the two host plugin channels — NativeView
2
- // (`_creatorUI.viewCall`, see ui/NativeView.ts) and headless services
3
- // (`_creatorUtils.serviceCall`, see runtime/service.ts). One protocol, two transports:
4
- // `args`, results, and event payloads travel as JSON strings, and an absent/empty string
5
- // decodes to `undefined`. Typed plugins (QRScanner, CameraView, Geolocation) layer methods
6
- // over this — see src/plugins/.
7
-
8
- /** @internal event-name → listeners. NativeView exposes it as `nvl` (host contract). */
9
- export type ChannelListeners = Record<string, ((data?: any) => void)[]>
10
-
11
- /** @internal */
12
- export const _channelOn = (listeners: ChannelListeners, event: string, callback: (data?: any) => void): void => {
13
- (listeners[event] ??= []).push(callback)
14
- }
15
-
16
- /** @internal */
17
- export const _channelOff = (listeners: ChannelListeners, event: string, callback: (data?: any) => void): void => {
18
- const list = listeners[event]
19
- if (!list) return
20
- const i = list.indexOf(callback)
21
- if (i >= 0) list.splice(i, 1)
22
- }
23
-
24
- /** @internal deliver a host event: decode the JSON payload, fan out (copy-before-iterate). */
25
- export const _channelEmit = (listeners: ChannelListeners, event: string, dataJson?: string): void => {
26
- const list = listeners[event]
27
- if (!list || list.length === 0) return
28
- const data = dataJson === undefined || dataJson === "" ? undefined : JSON.parse(dataJson)
29
- for (const cb of [...list]) cb(data)
30
- }
31
-
32
- type ChannelCallBridge = (
33
- id: number, method: string, args: string,
34
- onComplete: (result?: string) => void, onError: (err: string) => void,
35
- ) => void
36
-
37
- /** @internal one JSON call over a channel bridge.
38
- *
39
- * Takes the HANDLER OBJECT plus the member name — never a plucked-off function reference. On
40
- * Apple hosts the bridge members are JSExport-ed Objective-C/Swift methods, and JavaScriptCore
41
- * binds those to their native receiver: invoking a detached `handler.viewCall(…)` with no `this`
42
- * fails at runtime with "self type check failed for Objective-C instance method". QuickJS and web
43
- * hosts are plain functions that ignore `this`, so a detached call works there and the breakage
44
- * looks iOS/macOS-only. Calling through `owner` keeps the receiver attached on every host.
45
- *
46
- * `bridgeName` may be absent from the host object (optional ABI member) — that's the
47
- * unsupported-host path, not an error. */
48
- export const _channelCall = (
49
- owner: Record<string, any> | undefined, bridgeName: string, unsupportedError: string,
50
- id: number, method: string, args: any[],
51
- ): Promise<any> =>
52
- new Promise((res, rej) => {
53
- const bridge = owner?.[bridgeName] as ChannelCallBridge | undefined
54
- if (typeof bridge !== "function") {
55
- rej(new Error(unsupportedError))
56
- return
57
- }
58
- bridge.call(owner, id, method, JSON.stringify(args),
59
- (result?: string) => res(result === undefined || result === "" ? undefined : JSON.parse(result)),
60
- (err: string) => rej(new Error(err)))
61
- })
@@ -1,20 +0,0 @@
1
- // System clipboard (docs/app-lifecycle-plan.md). Write is fire-and-forget; read is async because
2
- // web gates it behind a permission prompt (native hosts complete synchronously).
3
-
4
- export const clipboard = {
5
- /** Put `text` on the system clipboard. Silent no-op on hosts without clipboard access. */
6
- write(text: string): void {
7
- _creatorUtils.clipboardWrite?.(text)
8
- },
9
- /** Read text from the system clipboard. Rejects when the platform has no clipboard access, the
10
- * user denied it, or there is nothing readable. */
11
- read(): Promise<string> {
12
- return new Promise((resolve, reject) => {
13
- if (typeof _creatorUtils.clipboardRead !== "function") {
14
- reject(new Error("Clipboard is not supported on this platform"))
15
- return
16
- }
17
- _creatorUtils.clipboardRead(resolve, (err: string) => reject(new Error(err)))
18
- })
19
- },
20
- }