lecodes-sdk 1.2.0 → 2.0.1
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.
- package/README.md +5 -2
- package/dist/editor.d.ts +12 -0
- package/dist/global.d.ts +4 -8
- package/dist/host.d.ts +2 -3
- package/dist/types/animate/tween/Animation.d.ts +0 -3
- package/dist/types/animate/tween/animateValue.d.ts +4 -2
- package/dist/types/animate/tween/easing.d.ts +8 -0
- package/dist/types/animate/tween/spec.d.ts +15 -7
- package/dist/types/audio/audio.d.ts +2 -1
- package/dist/types/canvas/Canvas.d.ts +40 -108
- package/dist/types/canvas/gen/cssColor.d.ts +17 -0
- package/dist/types/canvas/gen/recorder.d.ts +118 -0
- package/dist/types/canvas/gen/spec.d.ts +144 -0
- package/dist/types/core/color.d.ts +3 -1
- package/dist/types/core/pins.d.ts +18 -0
- package/dist/types/g2/Node2D.d.ts +5 -8
- package/dist/types/g2/Scene2D.d.ts +6 -2
- package/dist/types/gl/Foliage.d.ts +30 -7
- package/dist/types/gl/Light.d.ts +8 -0
- package/dist/types/gl/Lightmap.d.ts +13 -2
- package/dist/types/gl/Material.d.ts +17 -3
- package/dist/types/gl/Model.d.ts +6 -2
- package/dist/types/gl/Node.d.ts +3 -6
- package/dist/types/gl/Scene.d.ts +41 -13
- package/dist/types/gl/Texture.d.ts +1 -1
- package/dist/types/gl/animation/Locomotion.d.ts +8 -1
- package/dist/types/inject.d.ts +11 -11
- package/dist/types/inject.editor.d.ts +1 -0
- package/dist/types/net/core.d.ts +7 -0
- package/dist/types/plugin.d.ts +76 -0
- package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
- package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
- package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
- package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
- package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
- package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
- package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
- package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
- package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
- package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
- package/dist/types/runtime/app.d.ts +9 -2
- package/dist/types/runtime/fetch.d.ts +2 -0
- package/dist/types/runtime/input.d.ts +1 -1
- package/dist/types/runtime/media.d.ts +6 -10
- package/dist/types/runtime/misc.d.ts +4 -1
- package/dist/types/runtime/net.d.ts +3 -2
- package/dist/types/runtime/touch.d.ts +32 -0
- package/dist/types/scene/defineScene.d.ts +43 -2
- package/dist/types/scene/editor.d.ts +52 -0
- package/dist/types/scene/gizmos.d.ts +7 -4
- package/dist/types/ui/NativeView.d.ts +6 -4
- package/dist/types/ui/UI.d.ts +1 -1
- package/dist/types/ui/UIBottomSheet.d.ts +6 -12
- package/dist/types/ui/UIButton.d.ts +12 -14
- package/dist/types/ui/UIContainer.d.ts +0 -6
- package/dist/types/ui/UIImage.d.ts +1 -4
- package/dist/types/ui/UIInput.d.ts +8 -24
- package/dist/types/ui/UIModal.d.ts +0 -2
- package/dist/types/ui/UINode.d.ts +70 -56
- package/dist/types/ui/UIPager.d.ts +28 -27
- package/dist/types/ui/UIPopover.d.ts +0 -2
- package/dist/types/ui/UIScreen.d.ts +12 -17
- package/dist/types/ui/UIScrollable.d.ts +1 -4
- package/dist/types/ui/UIText.d.ts +0 -2
- package/dist/types/ui/UIVideo.d.ts +3 -5
- package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
- package/dist/types/ui/UIWidget.d.ts +10 -9
- package/dist/types/ui/colorKeys.gen.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +46 -32
- package/dist/types/ui/router.d.ts +18 -7
- package/dist/types/ui/styleColor.d.ts +1 -0
- package/dist/types/ui/transitions.d.ts +18 -0
- package/dist/types/ui/tree.d.ts +75 -0
- package/dist/types/version.d.ts +10 -0
- package/dist/types.json +1 -1
- package/package.json +12 -3
- package/prompts/2d.md +2 -6
- package/prompts/3d.md +1 -5
- package/prompts/README.md +1 -1
- package/prompts/canvas.md +9 -8
- package/prompts/compose.ts +1 -1
- package/prompts/core.md +3 -3
- package/prompts/design.md +1 -1
- package/prompts/dist/2d-game.md +473 -239
- package/prompts/dist/3d-app.md +553 -205
- package/prompts/dist/ar-app.md +435 -202
- package/prompts/dist/design.md +113 -95
- package/prompts/dist/ui-app.md +386 -170
- package/prompts/ui-design.md +2 -3
- package/prompts/ui.md +45 -37
- package/src/animate/tween/Animation.ts +34 -150
- package/src/animate/tween/Timeline.ts +175 -175
- package/src/animate/tween/animateValue.ts +6 -3
- package/src/animate/tween/easing.ts +10 -3
- package/src/animate/tween/spec.ts +41 -15
- package/src/audio/Sound.ts +3 -3
- package/src/audio/audio.ts +2 -1
- package/src/bridges/2d.d.ts +317 -0
- package/src/bridges/app.d.ts +91 -0
- package/src/bridges/audio.d.ts +97 -0
- package/src/bridges/canvas.d.ts +79 -0
- package/src/bridges/device.d.ts +72 -0
- package/src/bridges/fetch.d.ts +80 -0
- package/src/bridges/files.d.ts +70 -0
- package/src/bridges/gl.d.ts +1133 -0
- package/src/bridges/input.d.ts +72 -0
- package/src/bridges/media.d.ts +55 -0
- package/src/bridges/nav.d.ts +71 -0
- package/src/bridges/net.d.ts +52 -0
- package/src/bridges/service.d.ts +52 -0
- package/src/bridges/socket.d.ts +31 -0
- package/src/bridges/storage.d.ts +33 -0
- package/src/bridges/tree.d.ts +301 -0
- package/src/bridges/types.d.ts +49 -0
- package/src/canvas/Canvas.ts +114 -159
- package/src/canvas/gen/cssColor.ts +224 -0
- package/src/canvas/gen/recorder.ts +212 -0
- package/src/canvas/gen/spec.ts +201 -0
- package/src/chisel.ts +193 -0
- package/src/compile/assetMacro.ts +1 -1
- package/src/compile/bundler.ts +11 -2
- package/src/compile/compileProject.ts +43 -4
- package/src/compile/fontMacro.ts +3 -4
- package/src/compile/header.ts +26 -5
- package/src/compile/index.ts +3 -1
- package/src/compile/liteMaterial.ts +1 -1
- package/src/compile/sceneEditor.ts +11 -26
- package/src/core/color.ts +73 -30
- package/src/core/pins.ts +51 -0
- package/src/core/signals.ts +8 -1
- package/src/g2/CharacterController2D.ts +3 -3
- package/src/g2/Node2D.ts +57 -39
- package/src/g2/Physics2D.ts +2 -2
- package/src/g2/Scene2D.ts +35 -23
- package/src/g2/Texture2D.ts +1 -1
- package/src/g2/loop.ts +4 -4
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/Foliage.ts +72 -17
- package/src/gl/Geometry.ts +1 -2
- package/src/gl/Light.ts +10 -0
- package/src/gl/Lightmap.ts +45 -29
- package/src/gl/Material.ts +95 -51
- package/src/gl/Mesh.ts +120 -120
- package/src/gl/Model.ts +21 -16
- package/src/gl/Node.ts +91 -24
- package/src/gl/Particles.ts +1 -1
- package/src/gl/Scene.ts +100 -44
- package/src/gl/Texture.ts +8 -7
- package/src/gl/animation/AnimationClip.ts +1 -1
- package/src/gl/animation/DynamicBone.ts +482 -482
- package/src/gl/animation/Locomotion.ts +8 -3
- package/src/gl/nav/NavMesh.ts +3 -4
- package/src/gl/physics/Physics.ts +2 -2
- package/src/gl/physics/physicsEvents.ts +3 -3
- package/src/gl/scenarios.ts +291 -291
- package/src/gl/terrain/Terrain.ts +4 -5
- package/src/gl/touch.ts +14 -15
- package/src/host.d.ts +2 -3
- package/src/inject.editor.ts +7 -0
- package/src/inject.ts +13 -16
- package/src/net/core.ts +6 -5
- package/src/net/index.ts +1 -1
- package/src/net/replication.ts +1 -1
- package/src/plugin.ts +191 -0
- package/src/plugins/gen/camera/contract.d.ts +27 -0
- package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
- package/src/plugins/gen/camera/sdk/camera.ts +57 -0
- package/src/plugins/gen/geolocation/contract.d.ts +50 -0
- package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
- package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
- package/src/plugins/gen/map/contract.d.ts +144 -0
- package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
- package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
- package/src/plugins/gen/push/contract.d.ts +61 -0
- package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
- package/src/plugins/gen/push/sdk/push.ts +105 -0
- package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
- package/src/plugins/permission.ts +5 -4
- package/src/runtime/app.ts +20 -9
- package/src/runtime/appEvents.ts +5 -4
- package/src/runtime/channel.ts +18 -15
- package/src/runtime/clipboard.ts +4 -3
- package/src/runtime/datetime.ts +2 -1
- package/src/runtime/device.ts +17 -15
- package/src/runtime/fetch.ts +30 -20
- package/src/runtime/files.ts +16 -15
- package/src/runtime/input.ts +12 -10
- package/src/runtime/media.ts +50 -46
- package/src/runtime/misc.ts +7 -3
- package/src/runtime/net.ts +8 -7
- package/src/runtime/rpc.ts +1 -3
- package/src/runtime/service.ts +19 -14
- package/src/runtime/share.ts +4 -3
- package/src/runtime/storage.ts +6 -4
- package/src/runtime/touch.ts +32 -0
- package/src/scene/defineScene.ts +61 -365
- package/src/scene/editor.ts +408 -0
- package/src/scene/editorPlugins.ts +3 -3
- package/src/scene/gizmos.ts +15 -9
- package/src/server/db/marci/query.ts +1 -1
- package/src/server/host.ts +1 -1
- package/src/server/runtime.ts +1 -1
- package/src/ui/NativeView.ts +72 -25
- package/src/ui/UI.ts +3 -3
- package/src/ui/UIBottomSheet.ts +16 -17
- package/src/ui/UIButton.ts +54 -16
- package/src/ui/UIContainer.ts +0 -6
- package/src/ui/UIImage.ts +34 -30
- package/src/ui/UIInput.ts +29 -37
- package/src/ui/UIModal.ts +1 -3
- package/src/ui/UINode.ts +347 -297
- package/src/ui/UIPager.ts +93 -78
- package/src/ui/UIPopover.ts +0 -2
- package/src/ui/UIScreen.ts +41 -36
- package/src/ui/UIScrollable.ts +19 -13
- package/src/ui/UISpacer.ts +1 -1
- package/src/ui/UITabs.ts +8 -6
- package/src/ui/UIText.ts +7 -17
- package/src/ui/UIVideo.ts +30 -27
- package/src/ui/UIVirtualizedList.ts +58 -59
- package/src/ui/UIWidget.ts +38 -18
- package/src/ui/colorKeys.gen.ts +37 -0
- package/src/ui/fonts.ts +2 -2
- package/src/ui/presentable.ts +59 -42
- package/src/ui/router.ts +52 -35
- package/src/ui/styleColor.ts +56 -0
- package/src/ui/theme.ts +7 -6
- package/src/ui/transitions.ts +249 -0
- package/src/ui/tree.ts +346 -0
- package/src/version.ts +24 -0
- package/tests/helpers/engineWorld.ts +11 -0
- package/tests/helpers/fakeTree.ts +353 -0
- package/tests/helpers/hostStubs.ts +31 -0
- package/tests/helpers/index.ts +14 -0
- package/tests/helpers/memoryMarci.ts +124 -0
- package/tests/helpers/phases.ts +23 -0
- package/tests/helpers/preload.ts +18 -0
- package/tests/helpers/stubApp.ts +2 -0
- package/tests/helpers/stubDevice.ts +2 -0
- package/tests/helpers/stubFetch.ts +2 -0
- package/tests/helpers/stubInput.ts +2 -0
- package/dist/inject.js +0 -4629
- package/dist/types/core/registry.d.ts +0 -7
- package/dist/types/plugins/camera.d.ts +0 -25
- package/dist/types/plugins/push.d.ts +0 -46
- package/src/bridges.d.ts +0 -1769
- package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
- package/src/compile/__tests__/assetMacro.test.ts +0 -100
- package/src/compile/__tests__/assetName.test.ts +0 -55
- package/src/compile/__tests__/compile.test.ts +0 -310
- package/src/compile/__tests__/detectEntry.test.ts +0 -151
- package/src/compile/__tests__/fontMacro.test.ts +0 -199
- package/src/compile/__tests__/serverSplit.test.ts +0 -27
- package/src/core/__tests__/stateMachine.test.ts +0 -132
- package/src/core/registry.ts +0 -23
- package/src/plugins/camera.ts +0 -81
- package/src/plugins/push.ts +0 -132
- package/src/plugins/qr.ts +0 -73
package/src/gl/touch.ts
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
// the struck Node and to the active scene's listeners. Ported from worker/src/TouchEventSystem.ts.
|
|
3
3
|
|
|
4
4
|
import { ClickEvent, TouchStartEvent, type TrackHandler } from "../runtime/touch"
|
|
5
|
-
import {
|
|
5
|
+
import { ensureEmitter, setSceneTouchHandlers } from "../ui/tree"
|
|
6
|
+
import { nodes } from "./Node"
|
|
6
7
|
import { glState } from "./state"
|
|
7
8
|
|
|
8
9
|
// Hits inside a loaded model (GLB) land on sub-entities the user never created a Node for — walk up
|
|
9
10
|
// the transform hierarchy to the nearest entity someone actually holds (the model's Node).
|
|
10
11
|
const resolveHitNode = (entityId: number) => {
|
|
11
|
-
let node =
|
|
12
|
+
let node = nodes.get(entityId)
|
|
12
13
|
for (let id = entityId, i = 0; !node && i < 32; i++) {
|
|
13
14
|
id = _creator.getParent(id)
|
|
14
15
|
if (id === 0) break
|
|
15
|
-
node =
|
|
16
|
+
node = nodes.get(id)
|
|
16
17
|
}
|
|
17
18
|
return node ?? null
|
|
18
19
|
}
|
|
@@ -53,16 +54,14 @@ const onTouchStart = (pointerId: number, clientX: number, clientY: number, entit
|
|
|
53
54
|
return null
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
export const registerTouchEndEvent = (): void => {
|
|
65
|
-
if (touchEndRegistered) return
|
|
66
|
-
touchEndRegistered = true
|
|
67
|
-
_creatorUI.registerTouchEndEvent(onTouchEnd)
|
|
57
|
+
// The scene-level pointer dispatch rides the tree emitter (TREE_EVENT_SCENE_TOUCH_START / SCENE_CLICK):
|
|
58
|
+
// installed once, on the first listener.
|
|
59
|
+
let registered = false
|
|
60
|
+
const register = (): void => {
|
|
61
|
+
if (registered) return
|
|
62
|
+
registered = true
|
|
63
|
+
ensureEmitter()
|
|
64
|
+
setSceneTouchHandlers(onTouchStart, onTouchEnd)
|
|
68
65
|
}
|
|
66
|
+
export const registerTouchStartEvent = register
|
|
67
|
+
export const registerTouchEndEvent = register
|
package/src/host.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// JS-runtime globals provided by the host engine (web viewer / desktop / iOS), NOT by the SDK.
|
|
2
|
-
// Declared only so SDK and user code type-check — never bundled or injected.
|
|
3
|
-
// packages/projects/src/global.d.ts.
|
|
2
|
+
// Declared only so SDK and user code type-check — never bundled or injected.
|
|
4
3
|
|
|
5
4
|
declare global {
|
|
6
5
|
function setTimeout(handler: (...args: any[]) => void, timeout?: number): number
|
|
@@ -57,7 +56,7 @@ declare global {
|
|
|
57
56
|
* `Style<typeof myButton>`) is what you'd pass to `el.style(...)` — for typing reusable style
|
|
58
57
|
* presets. Deliberately no `Partial<>` wrapper: every style prop is already optional, and
|
|
59
58
|
* Partial over ClassStyles' `$`-index signature adds `| undefined`, breaking assignability back
|
|
60
|
-
* into `.style(...)`. Round-trip guarded by sdk/tests/ui
|
|
59
|
+
* into `.style(...)`. Round-trip guarded by sdk/tests/ui/types.test.ts. */
|
|
61
60
|
type Style<T> = T extends { style: (s: infer S) => any } ? S : never
|
|
62
61
|
}
|
|
63
62
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The SECOND inject module of an editor bundle (a compile with `editor: true` — bundler.ts adds it
|
|
2
|
+
// after inject.ts): what a scene editor's in-world half may name and an app may not. A shipped
|
|
3
|
+
// bundle is compiled without it, so nothing here can reach one.
|
|
4
|
+
//
|
|
5
|
+
// Same rule as inject.ts: the only file that exports these names.
|
|
6
|
+
|
|
7
|
+
export { sceneEditor as __sceneEditor, type SceneEditorApi } from "./scene/editor"
|
package/src/inject.ts
CHANGED
|
@@ -52,7 +52,7 @@ export { Bus, REVERB_PRESETS, type ReverbPreset, type ReverbParams, type EchoPar
|
|
|
52
52
|
export { audio, type PlaySoundOptions, type SpatialOptions, type Rolloff, type AudioStats } from "./audio/audio"
|
|
53
53
|
export { openFilePicker, files, type FileData } from "./runtime/files"
|
|
54
54
|
export { share } from "./runtime/share"
|
|
55
|
-
export { ClickEvent, TouchStartEvent, LongPressEvent } from "./runtime/touch"
|
|
55
|
+
export { ClickEvent, TouchStartEvent, LongPressEvent, MouseEnterEvent } from "./runtime/touch"
|
|
56
56
|
|
|
57
57
|
// ---- 2D engine (creator-2d) ----
|
|
58
58
|
export { Scene2D } from "./g2/Scene2D"
|
|
@@ -114,7 +114,7 @@ export {
|
|
|
114
114
|
// The Presentable navigation contract (docs/navigation-presentable-plan.md). `Presentable` is
|
|
115
115
|
// interface + runtime companion (declaration merging): `Presentable.current` = what's on screen.
|
|
116
116
|
export { Presentable } from "./ui/UI"
|
|
117
|
-
export type { PresentOptions, Transition, TransitionName,
|
|
117
|
+
export type { DismissOptions, PresentOptions, Transition, TransitionName, TransitionPose, TransitionSpec } from "./ui/UI"
|
|
118
118
|
// The base node type + the union a container accepts as children — documented as the signature of
|
|
119
119
|
// UIColumn/UIRow/append/etc., so they must be global (type-only; erased at runtime).
|
|
120
120
|
export type { UINode, UINodeChild } from "./ui/UINode"
|
|
@@ -135,26 +135,23 @@ export { type TweenMeta } from "./animate/tween/spec"
|
|
|
135
135
|
export { type EasingInput } from "./animate/tween/easing"
|
|
136
136
|
export { easeIn, easeOut, easeInOut } from "./animate/easings"
|
|
137
137
|
|
|
138
|
-
// ---- plugins (optional, host-provided capabilities — typed NativeView/service wrappers
|
|
139
|
-
|
|
140
|
-
export {
|
|
141
|
-
export {
|
|
138
|
+
// ---- plugins (optional, host-provided capabilities — typed NativeView/service wrappers). The first-party
|
|
139
|
+
// ones are VENDORED from the plugin repo (src/plugins/gen, `bun run vendor:plugins`); oauth / service are the SDK's. ----
|
|
140
|
+
export { QRScanner } from "./plugins/gen/qr-scanner/sdk/qr-scanner"
|
|
141
|
+
export { CameraView, type CameraFacing } from "./plugins/gen/camera/sdk/camera"
|
|
142
|
+
export { Geolocation, type GeoPosition, type GeoOptions, type GeoWatch } from "./plugins/gen/geolocation/sdk/geolocation"
|
|
142
143
|
export {
|
|
143
144
|
MapView, type MapOptions, type MapStyle, type MapStyleName, type MapCamera, type MapTap, type LngLat, type CameraMove, type FitOptions,
|
|
144
145
|
type MapPaddingValues, type MarkerItem, type MarkerTap, type MarkerLayer, type MarkerLayerOptions,
|
|
145
146
|
type LineLayer, type LineLayerOptions, type UserLocationOptions,
|
|
146
|
-
} from "./plugins/map"
|
|
147
|
-
export { Push, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/push"
|
|
147
|
+
} from "./plugins/gen/map/sdk/map"
|
|
148
|
+
export { Push, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/gen/push/sdk/push"
|
|
148
149
|
export { OAuth, type OAuthCredential, type OAuthProviderName } from "./plugins/oauth"
|
|
149
150
|
export { Service } from "./plugins/service"
|
|
150
151
|
|
|
151
152
|
// ---- scenes as data (.scene.ts files — see src/scene/defineScene.ts) ----
|
|
152
|
-
export { defineScene, use, ref, make, SceneHandle
|
|
153
|
-
export {
|
|
154
|
-
defineMaterial, MaterialHandle,
|
|
155
|
-
// editor-harness helpers (the scene editor's live material paths — not app API)
|
|
156
|
-
assignMaterialDef as __assignMaterialDef, applyMaterialParam as __applyMaterialParam,
|
|
157
|
-
} from "./scene/material"
|
|
153
|
+
export { defineScene, use, ref, make, SceneHandle } from "./scene/defineScene"
|
|
154
|
+
export { defineMaterial, MaterialHandle } from "./scene/material"
|
|
158
155
|
export type { MaterialData, MaterialParamValue, ShaderMaterialDef, LitMaterialDef, UnlitMaterialDef } from "./scene/material"
|
|
159
156
|
export type { SceneDef, SceneNodeDef, MeshDef, LightDef, MaterialDef, CameraNodeDef, AspectEntry, MakeEntry, LoadedScene, SceneInstance } from "./scene/defineScene"
|
|
160
157
|
export { CameraPlace } from "./gl/CameraPlace"
|
|
@@ -164,14 +161,14 @@ export type { LevelSpot, LevelBuild, LevelCollider, LevelLineOptions, LevelGridO
|
|
|
164
161
|
export type { FieldMeta } from "./core/fields"
|
|
165
162
|
// Editor-only line drawing from generator rebuild() / make() factories / tool hooks — drawn by the
|
|
166
163
|
// scene editor's viewport overlay, a no-op everywhere else (see src/scene/gizmos.ts).
|
|
167
|
-
export { Gizmos
|
|
164
|
+
export { Gizmos } from "./scene/gizmos"
|
|
168
165
|
export type { GizmoStyle, GizmoAnchor } from "./scene/gizmos"
|
|
169
166
|
|
|
170
167
|
// ---- no-code scenario aspects (attachable in the scene editor — see src/gl/scenarios.ts) ----
|
|
171
168
|
export { MoveTo, FollowPath, Spin, LookAt, PlayAnimation } from "./gl/scenarios"
|
|
172
169
|
|
|
173
170
|
// ---- editor plugins (called from `*.editor.ts` files — editor bundles only; DCE'd from apps) ----
|
|
174
|
-
export { registerEditorWindow, registerEditorTool
|
|
171
|
+
export { registerEditorWindow, registerEditorTool } from "./scene/editorPlugins"
|
|
175
172
|
export type { EditorApi, EditorRayHit, EditorToolHooks, EditorWindowFn } from "./scene/editorPlugins"
|
|
176
173
|
|
|
177
174
|
// ---- 3D engine (creator-gl) ----
|
package/src/net/core.ts
CHANGED
|
@@ -43,6 +43,8 @@ export type NetEvents = {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export type NetLaunch = { role: "server" | "client"; address: string; port: number; maxClients: number }
|
|
46
|
+
/** Link statistics (`player.rtt` / `player.loss` read them): the bridge's `NetStats`. */
|
|
47
|
+
export type NetStats = { rtt: number; loss: number; sentKbps: number; receivedKbps: number }
|
|
46
48
|
|
|
47
49
|
export const CH_RELIABLE = 0
|
|
48
50
|
export const CH_UNRELIABLE = 1
|
|
@@ -80,8 +82,8 @@ export class NetPlayer {
|
|
|
80
82
|
|
|
81
83
|
get isMe(): boolean { return state.me === this }
|
|
82
84
|
/** Round trip to this player in ms (server side) or to the server (our own player on a client). */
|
|
83
|
-
get rtt(): number { return infoOf(this)
|
|
84
|
-
get loss(): number { return infoOf(this)
|
|
85
|
+
get rtt(): number { return infoOf(this)?.rtt ?? 0 }
|
|
86
|
+
get loss(): number { return infoOf(this)?.loss ?? 0 }
|
|
85
87
|
|
|
86
88
|
/** Server → this player. */
|
|
87
89
|
send<T>(msg: NetMessage<T>, data: T): void {
|
|
@@ -162,10 +164,9 @@ export const emit = (event: keyof NetEvents, ...args: any[]): void => {
|
|
|
162
164
|
for (const fn of list.slice()) fn(...args)
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
const
|
|
166
|
-
const infoOf = (p: NetPlayer): Float32Array => {
|
|
167
|
+
const infoOf = (p: NetPlayer): NetStats | null => {
|
|
167
168
|
const b = bridge()
|
|
168
|
-
if (!b || p._slot === LOCAL_SLOT && state.role !== "client") return
|
|
169
|
+
if (!b || p._slot === LOCAL_SLOT && state.role !== "client") return null
|
|
169
170
|
return b.clientInfo(p._slot)
|
|
170
171
|
}
|
|
171
172
|
|
package/src/net/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ class NetImpl {
|
|
|
30
30
|
get players(): readonly NetPlayer[] { if (state.role !== "server") ensureMe(); return state.players }
|
|
31
31
|
player(id: number): NetPlayer | undefined { return state.players.find((p) => p.id === id) }
|
|
32
32
|
/** Our round trip to the server (client), 0 otherwise. */
|
|
33
|
-
get rtt(): number { return state.role === "client" ? (bridge()?.clientInfo(0)
|
|
33
|
+
get rtt(): number { return state.role === "client" ? (bridge()?.clientInfo(0)?.rtt ?? 0) : 0 }
|
|
34
34
|
/** The simulation tick — one per fixed step (`updateFixed`). The server's is THE tick; a client
|
|
35
35
|
* runs a few ticks ahead so its inputs arrive in time. */
|
|
36
36
|
get tick(): number { return currentTick() }
|
package/src/net/replication.ts
CHANGED
|
@@ -407,7 +407,7 @@ const sendSnapshots = (): void => {
|
|
|
407
407
|
|
|
408
408
|
// ---- client: snapshots in, inputs out, the clock, interpolation, correction ----------------------------
|
|
409
409
|
|
|
410
|
-
const rttTicks = (): number => (bridge()?.clientInfo(0)
|
|
410
|
+
const rttTicks = (): number => (bridge()?.clientInfo(0)?.rtt ?? 0) / TICK_MS
|
|
411
411
|
|
|
412
412
|
const onClientBinary = (channel: number, data: ArrayBuffer): void => {
|
|
413
413
|
if (data.byteLength < 12) return
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// `lecodes-sdk/plugin` — the one entry a PLUGIN imports the SDK through (docs/plans/plugins-plan.md).
|
|
2
|
+
// Two readers:
|
|
3
|
+
// - a plugin's `contract.d.ts`: the channel markers and the precise value types the generator maps
|
|
4
|
+
// to Swift / Kotlin;
|
|
5
|
+
// - its `sdk/` wrapper, generated (`<id>.gen.ts`) and hand-written (`<id>.ts`): the two channel
|
|
6
|
+
// bases and the few SDK internals a wrapper needs.
|
|
7
|
+
//
|
|
8
|
+
// A contract describes the WIRE of one channel, never the app-facing API: three interfaces per
|
|
9
|
+
// channel — the methods (it extends a marker), the params, the events.
|
|
10
|
+
|
|
11
|
+
import { File as HostFile } from "./runtime/fetch"
|
|
12
|
+
import { ServiceClient } from "./runtime/service"
|
|
13
|
+
import { NativeView, NativeViewElement } from "./ui/NativeView"
|
|
14
|
+
import { _navEpoch } from "./ui/presentable"
|
|
15
|
+
import { _requestCameraPermission } from "./plugins/permission"
|
|
16
|
+
|
|
17
|
+
export type { PresentOptions } from "./ui/presentable"
|
|
18
|
+
export type { NativeView } from "./ui/NativeView"
|
|
19
|
+
export type { FetchResponse } from "./runtime/fetch"
|
|
20
|
+
|
|
21
|
+
// ---- the contract's vocabulary ---------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/** Signed 32-bit integer on the wire. */
|
|
24
|
+
export type i32 = number
|
|
25
|
+
/** Double on the wire — also every integer that can leave 32 bits (a millisecond time stamp). */
|
|
26
|
+
export type f64 = number
|
|
27
|
+
|
|
28
|
+
/** Bytes that STAY in the host's buffer table: the native half hands over its bytes + a name, the
|
|
29
|
+
* app gets the SDK's `File` handle (a texture source, a `FormData` value, a share) and the bytes
|
|
30
|
+
* never enter the JS heap. For bytes the app must read itself, a contract says `Uint8Array`. */
|
|
31
|
+
export type File = HostFile
|
|
32
|
+
|
|
33
|
+
/** Any JSON value, opaque to the generator (GeoJSON, a developer's own payload): it crosses as is
|
|
34
|
+
* and the native half receives the platform's untyped value. */
|
|
35
|
+
export type Json = null | boolean | number | string | Json[] | { [key: string]: Json }
|
|
36
|
+
|
|
37
|
+
declare const channel: unique symbol
|
|
38
|
+
|
|
39
|
+
/** Marks a contract interface as a NativeView channel. `Name` is what the halves register under
|
|
40
|
+
* (`registerView(name)`); `Params` is what a factory receives; `Events` maps an event's name to
|
|
41
|
+
* its payload (`void` = no payload). */
|
|
42
|
+
export interface View<Name extends string, Params extends object = {}, Events extends object = {}> {
|
|
43
|
+
readonly [channel]?: { kind: "view", name: Name, params: Params, events: Events }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Marks a contract interface as a headless Service channel — see {@link View}. */
|
|
47
|
+
export interface Service<Name extends string, Params extends object = {}, Events extends object = {}> {
|
|
48
|
+
readonly [channel]?: { kind: "service", name: Name, params: Params, events: Events }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ---- the wrapper's bases ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
/** A listener of the event `K`: no argument for a `void` payload. */
|
|
54
|
+
export type Listener<Events, K extends keyof Events> = Events[K] extends void ? () => void : (payload: Events[K]) => void
|
|
55
|
+
|
|
56
|
+
/** A method the host's half is too old for (`@since`): refused HERE, by what it needs, before the
|
|
57
|
+
* half is asked. `version` = the half's contract version (0 = the host has none: the channel's own
|
|
58
|
+
* "not supported" speaks then). */
|
|
59
|
+
const tooOld = (iface: string, name: string, method: string, since: number, version: number): Error | null =>
|
|
60
|
+
since > 1 && version > 0 && version < since
|
|
61
|
+
? new Error(`${iface}.${method} needs the "${name}" plugin at contract version ${since} — this host's is ${version}`)
|
|
62
|
+
: null
|
|
63
|
+
|
|
64
|
+
/** An optional argument the caller left out is ABSENT on the wire, not a null in its place. */
|
|
65
|
+
const present = (args: any[]): any[] => {
|
|
66
|
+
let n = args.length
|
|
67
|
+
while (n > 0 && args[n - 1] === undefined) n--
|
|
68
|
+
return n === args.length ? args : args.slice(0, n)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** What an event's payload is read with when it is not on the wire what it is for the app (it
|
|
72
|
+
* holds a File): by the event's name. */
|
|
73
|
+
export type EventReaders = { [event: string]: (wire: any) => any }
|
|
74
|
+
|
|
75
|
+
/** The listeners of the events that have a reader: the app's callback → the one that reads first,
|
|
76
|
+
* so `off` finds what `on` registered. */
|
|
77
|
+
class Readers {
|
|
78
|
+
private readonly wrapped = new Map<string, Map<Function, (data?: any) => void>>()
|
|
79
|
+
private readonly readers: EventReaders | undefined
|
|
80
|
+
constructor(readers: EventReaders | undefined) { this.readers = readers }
|
|
81
|
+
|
|
82
|
+
on(event: string, callback: (data?: any) => void): (data?: any) => void {
|
|
83
|
+
const read = this.readers?.[event]
|
|
84
|
+
if (!read) return callback
|
|
85
|
+
let byCallback = this.wrapped.get(event)
|
|
86
|
+
if (!byCallback) this.wrapped.set(event, byCallback = new Map())
|
|
87
|
+
let listener = byCallback.get(callback)
|
|
88
|
+
if (!listener) byCallback.set(callback, listener = (wire?: any) => callback(read(wire)))
|
|
89
|
+
return listener
|
|
90
|
+
}
|
|
91
|
+
off(event: string, callback: (data?: any) => void): (data?: any) => void {
|
|
92
|
+
const byCallback = this.wrapped.get(event)
|
|
93
|
+
const listener = byCallback?.get(callback)
|
|
94
|
+
if (!listener) return callback
|
|
95
|
+
byCallback!.delete(callback)
|
|
96
|
+
return listener
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The element of a view plugin: a `NativeView` whose events are the contract's. Generated wrappers
|
|
101
|
+
* extend it (`<Name>Base`); a hand-written one extends that, or this when its API is not the wire's. */
|
|
102
|
+
export class ViewChannel<Events extends object = {}> extends NativeViewElement {
|
|
103
|
+
/** @internal The contract interface's name, for messages. */
|
|
104
|
+
readonly _iface: string
|
|
105
|
+
private readonly _readers: Readers
|
|
106
|
+
|
|
107
|
+
constructor(iface: string, name: string, params: object, readers?: EventReaders) {
|
|
108
|
+
super(name, params)
|
|
109
|
+
this._iface = iface
|
|
110
|
+
this._readers = new Readers(readers)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Whether this host registered a view under `name` — at contract version `since` or later, when
|
|
114
|
+
* one is given (a method's `@since`). */
|
|
115
|
+
static supports(name: string, since = 1): boolean { return since <= 1 ? NativeView.isSupported(name) : NativeView.version(name) >= since }
|
|
116
|
+
|
|
117
|
+
override on<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
|
|
118
|
+
return super.on(event, this._readers.on(event, callback as (data?: any) => void))
|
|
119
|
+
}
|
|
120
|
+
override off<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
|
|
121
|
+
return super.off(event, this._readers.off(event, callback as (data?: any) => void))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @internal One call over the wire — what the generated functions are made of. */
|
|
125
|
+
_call(method: string, args: any[], since = 1): Promise<any> {
|
|
126
|
+
const old = since > 1 ? tooOld(this._iface, this.viewName, method, since, NativeView.version(this.viewName)) : null
|
|
127
|
+
return old ? Promise.reject(old) : this._channelSend(method, present(args))
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The client of a service plugin: a session that opens on the first call, with the contract's
|
|
132
|
+
* events. The service twin of {@link ViewChannel}. */
|
|
133
|
+
export class ServiceChannel<Events extends object = {}> extends ServiceClient {
|
|
134
|
+
/** @internal */
|
|
135
|
+
readonly _iface: string
|
|
136
|
+
private readonly _readers: Readers
|
|
137
|
+
|
|
138
|
+
constructor(iface: string, name: string, params: object = {}, readers?: EventReaders) {
|
|
139
|
+
super(name, params)
|
|
140
|
+
this._iface = iface
|
|
141
|
+
this._readers = new Readers(readers)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Whether this host registered a service under `name` — at contract version `since` or later,
|
|
145
|
+
* when one is given (a method's `@since`). */
|
|
146
|
+
static supports(name: string, since = 1): boolean { return since <= 1 ? ServiceClient.isSupported(name) : ServiceClient.version(name) >= since }
|
|
147
|
+
|
|
148
|
+
override on<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
|
|
149
|
+
return super.on(event, this._readers.on(event, callback as (data?: any) => void))
|
|
150
|
+
}
|
|
151
|
+
override off<K extends keyof Events & string>(event: K, callback: Listener<Events, K>): this {
|
|
152
|
+
return super.off(event, this._readers.off(event, callback as (data?: any) => void))
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @internal */
|
|
156
|
+
_call(method: string, args: any[], since = 1): Promise<any> {
|
|
157
|
+
const old = since > 1 ? tooOld(this._iface, this.serviceName, method, since, ServiceClient.version(this.serviceName)) : null
|
|
158
|
+
return old ? Promise.reject(old) : this._channelSend(method, present(args))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ---- what a wrapper reads off the wire -------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
/** @internal A `File` of the contract as it crosses: the host buffer's id, the name, the size. */
|
|
165
|
+
export const _toFile = (wire: { systemId: number, name: string, size: number }): File =>
|
|
166
|
+
new HostFile(wire.systemId, wire.name, wire.size)
|
|
167
|
+
|
|
168
|
+
/** @internal …and the other way: a File the app hands a half. The bytes stay where they are — the
|
|
169
|
+
* half reads them out of the host's buffer table. */
|
|
170
|
+
export const _fromFile = (file: File): { systemId: number, name: string, size: number } =>
|
|
171
|
+
({ systemId: file._h.id, name: file.name, size: file.size })
|
|
172
|
+
|
|
173
|
+
// ---- for the hand-written part ---------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
/** The text of a host buffer — what a bundled `asset()` is in a shell compile (`"id:N"`): the host
|
|
176
|
+
* already holds the bytes. Empty when there is no such buffer. */
|
|
177
|
+
export const bufferText = (systemId: number): string => _creatorFetch.toText(systemId)
|
|
178
|
+
|
|
179
|
+
/** Prepare-then-present for a view that needs the camera: fail before any prompt on a host without
|
|
180
|
+
* the view, ask for the permission while the current destination stays visible, give up when
|
|
181
|
+
* another navigation superseded this one. Resolves when the view may open. */
|
|
182
|
+
export const prepareCamera = async (view: ViewChannel<any>): Promise<void> => {
|
|
183
|
+
if (!NativeView.isSupported(view.viewName)) {
|
|
184
|
+
throw new Error(`${view._iface} isn't available on this host — it needs a registered "${view.viewName}" view.`)
|
|
185
|
+
}
|
|
186
|
+
const epoch = _navEpoch()
|
|
187
|
+
await _requestCameraPermission()
|
|
188
|
+
if (_navEpoch() !== epoch) {
|
|
189
|
+
throw new Error(`${view._iface}.open() superseded: another destination was opened while preparing`)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/camera/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
|
+
// camera — a live camera preview with still capture.
|
|
4
|
+
// The contract of the WIRE: what the app-facing `CameraView` (sdk/) and the halves (ios/, android/,
|
|
5
|
+
// web/) agree on. The generator reads this file; nothing else describes the channel.
|
|
6
|
+
|
|
7
|
+
import type { File, View } from "../../../plugin"
|
|
8
|
+
|
|
9
|
+
export type Facing = "front" | "back"
|
|
10
|
+
|
|
11
|
+
export interface CameraParams {
|
|
12
|
+
/** Default "back". */
|
|
13
|
+
facingMode?: Facing
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CameraEvents {
|
|
17
|
+
/** The preview could not start or stopped (no camera, the stream was refused). */
|
|
18
|
+
error: { message: string }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CameraView extends View<"camera", CameraParams, CameraEvents> {
|
|
22
|
+
/** Capture a still frame as a JPEG.
|
|
23
|
+
* @rejects failed */
|
|
24
|
+
takePhoto(): Promise<File>
|
|
25
|
+
/** Switch the camera while the preview is live. */
|
|
26
|
+
setFacingMode(mode: Facing): Promise<void>
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/camera/sdk/camera.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
|
+
// camera: the view "camera", contract version 1.
|
|
5
|
+
|
|
6
|
+
import { ViewChannel, _toFile, type File } from "../../../../plugin"
|
|
7
|
+
import type { CameraEvents, CameraParams, Facing } from "../contract"
|
|
8
|
+
|
|
9
|
+
/** What the halves register under. */
|
|
10
|
+
export const NAME = "camera"
|
|
11
|
+
/** The contract's version: the highest `@since` of its members. */
|
|
12
|
+
export const VERSION = 1
|
|
13
|
+
|
|
14
|
+
// ---- the wire, call by call ----------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
/** Capture a still frame as a JPEG.
|
|
17
|
+
* Rejects with "failed". */
|
|
18
|
+
export const takePhoto = (view: ViewChannel<CameraEvents>): Promise<File> =>
|
|
19
|
+
view._call("takePhoto", []).then((w: any) => _toFile(w))
|
|
20
|
+
|
|
21
|
+
/** Switch the camera while the preview is live. */
|
|
22
|
+
export const setFacingMode = (view: ViewChannel<CameraEvents>, mode: Facing): Promise<void> =>
|
|
23
|
+
view._call("setFacingMode", [mode])
|
|
24
|
+
|
|
25
|
+
// ---- what the host's half has --------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/** A method of the contract. */
|
|
28
|
+
export type Method = "takePhoto" | "setFacingMode"
|
|
29
|
+
|
|
30
|
+
/** Whether this host has the view "camera" — and, given a method, a half new enough for it
|
|
31
|
+
* (a method added after the first version of the contract is tagged `@since`). */
|
|
32
|
+
export const supports = (method?: Method): boolean =>
|
|
33
|
+
ViewChannel.supports(NAME) // every method is of version 1
|
|
34
|
+
|
|
35
|
+
// ---- the element ----------------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/** The view "camera" with the wire's methods as its own. */
|
|
38
|
+
export class CameraViewBase extends ViewChannel<CameraEvents> {
|
|
39
|
+
constructor(params: CameraParams = {}) { super("CameraView", NAME, params) }
|
|
40
|
+
|
|
41
|
+
/** Capture a still frame as a JPEG.
|
|
42
|
+
* Rejects with "failed". */
|
|
43
|
+
takePhoto(): Promise<File> { return takePhoto(this) }
|
|
44
|
+
/** Switch the camera while the preview is live. */
|
|
45
|
+
setFacingMode(mode: Facing): Promise<void> { return setFacingMode(this, mode) }
|
|
46
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/camera/sdk/camera.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
|
+
// CameraView — the device camera (a live preview + still capture) as a view the app opens
|
|
4
|
+
// fullscreen (`await camera.open()`), pushes (`Router.push(camera)`) or embeds among a screen's
|
|
5
|
+
// children. `takePhoto()` returns a `File`: a texture source, a `FormData` value, a share.
|
|
6
|
+
//
|
|
7
|
+
// Hand-written over the generated wire for ONE reason: the permission is asked BEFORE the view is
|
|
8
|
+
// presented, while the current destination stays visible.
|
|
9
|
+
|
|
10
|
+
import { prepareCamera, type File, type NativeView, type PresentOptions } from "../../../../plugin"
|
|
11
|
+
import { CameraViewBase, NAME } from "./camera.gen"
|
|
12
|
+
import type { Facing } from "../contract"
|
|
13
|
+
|
|
14
|
+
export type CameraFacing = Facing
|
|
15
|
+
|
|
16
|
+
export interface CameraView extends NativeView {
|
|
17
|
+
/** Request the camera permission, then present the preview. The current destination stays
|
|
18
|
+
* visible until the camera is ready; rejects if the permission is denied, the host has no
|
|
19
|
+
* camera, or another navigation superseded this one. */
|
|
20
|
+
open(options?: PresentOptions): Promise<void>
|
|
21
|
+
/** Capture a still frame as an image `File` (host-encoded). */
|
|
22
|
+
takePhoto(): Promise<File>
|
|
23
|
+
/** Switch between the front and the back camera while the preview is live. */
|
|
24
|
+
setFacingMode(facingMode: CameraFacing): Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class CameraViewElement extends CameraViewBase {
|
|
28
|
+
constructor(options: { facingMode?: CameraFacing } = {}) {
|
|
29
|
+
super({ facingMode: options.facingMode ?? "back" })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** @internal prepare-then-present (see Presentable._prepare). */
|
|
33
|
+
_prepare(): Promise<void> { return prepareCamera(this) }
|
|
34
|
+
|
|
35
|
+
override async open(options?: PresentOptions): Promise<void> {
|
|
36
|
+
await this._prepare()
|
|
37
|
+
super.open(options)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a camera view (default: the back camera). `CameraView.isSupported` reports whether this
|
|
43
|
+
* host has the capability — check it before offering the feature.
|
|
44
|
+
*/
|
|
45
|
+
// PURE IIFE so an app that never uses the camera tree-shakes the whole plugin away.
|
|
46
|
+
export const CameraView: {
|
|
47
|
+
(options?: { facingMode?: CameraFacing }): CameraView
|
|
48
|
+
/** Whether this host registered a "camera" view. */
|
|
49
|
+
readonly isSupported: boolean
|
|
50
|
+
} = /*#__PURE__*/ (() => {
|
|
51
|
+
const factory = (options?: { facingMode?: CameraFacing }): CameraView =>
|
|
52
|
+
new CameraViewElement(options) as unknown as CameraView
|
|
53
|
+
Object.defineProperty(factory, "isSupported", {
|
|
54
|
+
get: (): boolean => CameraViewBase.supports(NAME),
|
|
55
|
+
})
|
|
56
|
+
return factory as any
|
|
57
|
+
})()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/geolocation/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
|
+
// geolocation — the device position.
|
|
4
|
+
// The wire has ONE watch per session (startWatch / stopWatch + the `position` event); the
|
|
5
|
+
// app-facing `Geolocation.watch(callback)` fans it out to its subscribers in sdk/.
|
|
6
|
+
//
|
|
7
|
+
// A session opens without a prompt. The OS permission prompt belongs to the first call that needs
|
|
8
|
+
// the position.
|
|
9
|
+
|
|
10
|
+
import type { Service, f64 } from "../../../plugin"
|
|
11
|
+
|
|
12
|
+
export interface GeoOptions {
|
|
13
|
+
/** Prefer the precise (GPS) provider — slower first fix, more battery. Default false. */
|
|
14
|
+
highAccuracy?: boolean
|
|
15
|
+
/** getCurrent only: reject with "timeout" when no fix arrives in this many ms. */
|
|
16
|
+
timeout?: f64
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GeoPosition {
|
|
20
|
+
latitude: f64
|
|
21
|
+
longitude: f64
|
|
22
|
+
/** Horizontal accuracy radius, meters. */
|
|
23
|
+
accuracy: f64
|
|
24
|
+
/** Meters above sea level; null when the provider does not know. */
|
|
25
|
+
altitude: f64 | null
|
|
26
|
+
/** Direction of travel, degrees clockwise from north; null when stationary or unknown. */
|
|
27
|
+
heading: f64 | null
|
|
28
|
+
/** Ground speed, m/s; null when unknown. */
|
|
29
|
+
speed: f64 | null
|
|
30
|
+
/** When the fix was taken, ms since the epoch. */
|
|
31
|
+
timestamp: f64
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface GeolocationParams {}
|
|
35
|
+
|
|
36
|
+
export interface GeolocationEvents {
|
|
37
|
+
/** A fix of the live watch. */
|
|
38
|
+
position: GeoPosition
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Geolocation extends Service<"geolocation", GeolocationParams, GeolocationEvents> {
|
|
42
|
+
/** One fix.
|
|
43
|
+
* @rejects denied unavailable timeout */
|
|
44
|
+
getCurrent(options?: GeoOptions): Promise<GeoPosition>
|
|
45
|
+
/** Start the session's watch; resolves once it is live (permission granted, provider started).
|
|
46
|
+
* A second call while watching resolves at once and keeps the first call's options.
|
|
47
|
+
* @rejects denied unavailable */
|
|
48
|
+
startWatch(options?: GeoOptions): Promise<void>
|
|
49
|
+
stopWatch(): Promise<void>
|
|
50
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// GENERATED by scripts/vendor-plugins.ts from lecodes-plugins/plugins/geolocation/sdk/geolocation.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
|
+
// geolocation: the service "geolocation", contract version 1.
|
|
5
|
+
|
|
6
|
+
import { ServiceChannel } from "../../../../plugin"
|
|
7
|
+
import type { GeoOptions, GeoPosition, GeolocationEvents, GeolocationParams } from "../contract"
|
|
8
|
+
|
|
9
|
+
/** What the halves register under. */
|
|
10
|
+
export const NAME = "geolocation"
|
|
11
|
+
/** The contract's version: the highest `@since` of its members. */
|
|
12
|
+
export const VERSION = 1
|
|
13
|
+
|
|
14
|
+
// ---- the wire, call by call ----------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
/** One fix.
|
|
17
|
+
* Rejects with "denied" / "unavailable" / "timeout". */
|
|
18
|
+
export const getCurrent = (service: ServiceChannel<GeolocationEvents>, options?: GeoOptions): Promise<GeoPosition> =>
|
|
19
|
+
service._call("getCurrent", [options])
|
|
20
|
+
|
|
21
|
+
/** Start the session's watch; resolves once it is live (permission granted, provider started).
|
|
22
|
+
* A second call while watching resolves at once and keeps the first call's options.
|
|
23
|
+
* Rejects with "denied" / "unavailable". */
|
|
24
|
+
export const startWatch = (service: ServiceChannel<GeolocationEvents>, options?: GeoOptions): Promise<void> =>
|
|
25
|
+
service._call("startWatch", [options])
|
|
26
|
+
|
|
27
|
+
export const stopWatch = (service: ServiceChannel<GeolocationEvents>): Promise<void> =>
|
|
28
|
+
service._call("stopWatch", [])
|
|
29
|
+
|
|
30
|
+
// ---- what the host's half has --------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/** A method of the contract. */
|
|
33
|
+
export type Method = "getCurrent" | "startWatch" | "stopWatch"
|
|
34
|
+
|
|
35
|
+
/** Whether this host has the service "geolocation" — and, given a method, a half new enough for it
|
|
36
|
+
* (a method added after the first version of the contract is tagged `@since`). */
|
|
37
|
+
export const supports = (method?: Method): boolean =>
|
|
38
|
+
ServiceChannel.supports(NAME) // every method is of version 1
|
|
39
|
+
|
|
40
|
+
// ---- the client -----------------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/** The service "geolocation" with the wire's methods as its own. */
|
|
43
|
+
export class GeolocationBase extends ServiceChannel<GeolocationEvents> {
|
|
44
|
+
constructor(params: GeolocationParams = {}) { super("Geolocation", NAME, params) }
|
|
45
|
+
|
|
46
|
+
/** One fix.
|
|
47
|
+
* Rejects with "denied" / "unavailable" / "timeout". */
|
|
48
|
+
getCurrent(options?: GeoOptions): Promise<GeoPosition> { return getCurrent(this, options) }
|
|
49
|
+
/** Start the session's watch; resolves once it is live (permission granted, provider started).
|
|
50
|
+
* A second call while watching resolves at once and keeps the first call's options.
|
|
51
|
+
* Rejects with "denied" / "unavailable". */
|
|
52
|
+
startWatch(options?: GeoOptions): Promise<void> { return startWatch(this, options) }
|
|
53
|
+
stopWatch(): Promise<void> { return stopWatch(this) }
|
|
54
|
+
}
|