@reactvision/react-viro 2.55.0 → 2.57.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.
- package/README.md +27 -0
- package/android/react_viro/react_viro-release.aar +0 -0
- package/android/viro_renderer/viro_renderer-release.aar +0 -0
- package/components/AR/ViroARScene.tsx +17 -0
- package/components/AR/ViroARSceneNavigator.tsx +33 -0
- package/components/Utilities/StreamingAudioManager.ts +73 -0
- package/components/Utilities/ViroVersion.ts +1 -1
- package/components/ViroCameraTexture.tsx +230 -0
- package/components/ViroGameLoop.tsx +74 -0
- package/components/ViroGameLoopUtils.ts +52 -0
- package/components/ViroObjectDetector.tsx +222 -0
- package/components/ViroVirtualButton.tsx +101 -0
- package/components/ViroVirtualJoystick.tsx +126 -0
- package/components/hooks/useGameLoop.ts +78 -0
- package/dist/components/AR/ViroARScene.d.ts +12 -0
- package/dist/components/AR/ViroARScene.js +8 -1
- package/dist/components/AR/ViroARSceneNavigator.d.ts +30 -0
- package/dist/components/Utilities/StreamingAudioManager.d.ts +22 -0
- package/dist/components/Utilities/StreamingAudioManager.js +45 -0
- package/dist/components/Utilities/ViroVersion.d.ts +1 -1
- package/dist/components/Utilities/ViroVersion.js +1 -1
- package/dist/components/ViroCameraTexture.d.ts +137 -0
- package/dist/components/ViroCameraTexture.js +170 -0
- package/dist/components/ViroGameLoop.d.ts +31 -0
- package/dist/components/ViroGameLoop.js +31 -0
- package/dist/components/ViroGameLoopUtils.d.ts +26 -0
- package/dist/components/ViroGameLoopUtils.js +44 -0
- package/dist/components/ViroObjectDetector.d.ts +138 -0
- package/dist/components/ViroObjectDetector.js +85 -0
- package/dist/components/ViroVirtualButton.d.ts +76 -0
- package/dist/components/ViroVirtualButton.js +62 -0
- package/dist/components/ViroVirtualJoystick.d.ts +85 -0
- package/dist/components/ViroVirtualJoystick.js +72 -0
- package/dist/components/hooks/useGameLoop.d.ts +34 -0
- package/dist/components/hooks/useGameLoop.js +63 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +20 -2
- package/dist/plugins/withViro.js +2 -15
- package/dist/plugins/withViroAndroid.js +52 -15
- package/dist/plugins/withViroIos.js +0 -6
- package/index.ts +21 -0
- package/ios/ViroReact.podspec +25 -6
- package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreCoreMLSemanticsResources.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARDepthMesh.h +9 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrame.h +33 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrameiOS.h +26 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARScene.h +15 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSceneDelegateiOS.h +8 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSession.h +7 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSessioniOS.h +18 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARWorldMesh.h +111 -8
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROAudioPlayer.h +19 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROAudioPlayerStreamiOS.h +95 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROCameraTextureiOS.h +17 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODriver.h +20 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODynamicGeometry.h +170 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODynamicMeshNode.h +87 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGLTFLoader.h +5 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGameLoopListener.h +125 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputState.h +133 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROMonocularDepthEstimator.h +73 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROObjectDetector.h +134 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPCMRingBuffer.h +143 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPencil.h +9 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderContext.h +4 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderer.h +1 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVertexBuffer.h +102 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROViewAR.h +9 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVirtualControllerRegistry.h +106 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Shaders.dat +1 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/constant_fsh.glsl +1 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/constant_fsh_adreno.glsl +1 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/standard_fsh.glsl +1 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/standard_vsh.glsl +4 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/standard_vsh_adreno.glsl +4 -0
- package/ios/dist/ViroRendererVisionOS/ViroKit.podspec +27 -0
- package/ios/dist/include/VRTARScene.h +1 -0
- package/ios/dist/include/VRTARSceneNavigator.h +3 -0
- package/ios/dist/include/VRTCameraTexture.h +62 -0
- package/ios/dist/include/VRTCameraTextureManager.h +30 -0
- package/ios/dist/include/VRTCameraTextureModule.h +38 -0
- package/ios/dist/include/VRTGameLoopView.h +19 -0
- package/ios/dist/include/VRTGameLoopViewManager.h +8 -0
- package/ios/dist/include/VRTManagedAnimation.h +67 -0
- package/ios/dist/include/VRTStreamingAudioModule.h +21 -0
- package/ios/dist/include/VRTVirtualButtonView.h +47 -0
- package/ios/dist/include/VRTVirtualButtonViewManager.h +21 -0
- package/ios/dist/include/VRTVirtualJoystickView.h +59 -0
- package/ios/dist/include/VRTVirtualJoystickViewManager.h +26 -0
- package/ios/dist/lib/libViroReact.a +0 -0
- package/package.json +17 -20
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-present, ReactVision, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import {
|
|
11
|
+
NativeSyntheticEvent,
|
|
12
|
+
requireNativeComponent,
|
|
13
|
+
ViewProps,
|
|
14
|
+
} from "react-native";
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Types
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export type ViroDetectorMode = "prompt-free" | "text" | "visual";
|
|
21
|
+
|
|
22
|
+
export type ViroDetectionBoundingBox = {
|
|
23
|
+
/** Left edge, normalized [0, 1] */
|
|
24
|
+
x: number;
|
|
25
|
+
/** Top edge, normalized [0, 1] */
|
|
26
|
+
y: number;
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ViroDetectedObject = {
|
|
32
|
+
label: string;
|
|
33
|
+
confidence: number;
|
|
34
|
+
boundingBox: ViroDetectionBoundingBox;
|
|
35
|
+
/** 3D world position (metres), from raycasting the bbox centre. Present when `projectToWorld` is true (iOS only). */
|
|
36
|
+
worldPosition?: { x: number; y: number; z: number };
|
|
37
|
+
/**
|
|
38
|
+
* Bounding box in density-independent points (dp), aligned to the on-screen AR
|
|
39
|
+
* camera preview. Use directly as { left, top, width, height } in an
|
|
40
|
+
* absolute-positioned View. Present on iOS and Android.
|
|
41
|
+
*/
|
|
42
|
+
screenBoundingBox?: { x: number; y: number; width: number; height: number };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type ViroDetectionEvent = {
|
|
46
|
+
detections: ViroDetectedObject[];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ViroDetectorReadyEvent = Record<string, never>;
|
|
50
|
+
|
|
51
|
+
export type ViroDetectorErrorEvent = {
|
|
52
|
+
error: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type Props = ViewProps & {
|
|
56
|
+
/**
|
|
57
|
+
* The YOLOE model to run. Either a bundled model **name** (resolved natively as
|
|
58
|
+
* `<name>.onnx` in the app bundle / Android assets), or an absolute `/`-path or
|
|
59
|
+
* `file://` URL to an `.onnx` file. Defaults to "yoloe-26s".
|
|
60
|
+
*/
|
|
61
|
+
model?: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Inference mode:
|
|
65
|
+
* - "prompt-free": YOLOE LRPC — detects 4,585 categories with no extra prompt.
|
|
66
|
+
* - "text": YOLOE RepRTA — detect only the classes listed in `categories`.
|
|
67
|
+
* - "visual": YOLOE SAVPE — detect objects similar to a reference crop
|
|
68
|
+
* (reference image API to be added in a future release).
|
|
69
|
+
* Defaults to "prompt-free".
|
|
70
|
+
*/
|
|
71
|
+
mode?: ViroDetectorMode;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Text categories for "text" mode, e.g. ["chair", "person", "laptop"].
|
|
75
|
+
* Ignored in other modes.
|
|
76
|
+
*/
|
|
77
|
+
categories?: string[];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Minimum confidence score [0, 1] for a detection to be reported.
|
|
81
|
+
* Defaults to 0.4.
|
|
82
|
+
*/
|
|
83
|
+
confidenceThreshold?: number;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* IoU threshold used by NMS post-processing.
|
|
87
|
+
* Defaults to 0.45.
|
|
88
|
+
*/
|
|
89
|
+
iouThreshold?: number;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Maximum number of inference calls per second. The camera runs at the
|
|
93
|
+
* device's native frame rate; this throttle prevents the inference thread
|
|
94
|
+
* from saturating the CPU/NPU while the AR renderer runs in parallel.
|
|
95
|
+
* Defaults to 15.
|
|
96
|
+
*/
|
|
97
|
+
maxFPS?: number;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Maximum number of detections reported per frame, kept as the top-N by
|
|
101
|
+
* confidence (after NMS). Lower this to reduce clutter, raise it to surface
|
|
102
|
+
* more objects.
|
|
103
|
+
* Defaults to 20.
|
|
104
|
+
*/
|
|
105
|
+
maxDetections?: number;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* When true, each detection includes a `worldPosition` {x, y, z} obtained by
|
|
109
|
+
* raycasting the bbox centre against the AR scene. iOS only (Android emits
|
|
110
|
+
* `screenBoundingBox` but not yet `worldPosition`).
|
|
111
|
+
* Defaults to true.
|
|
112
|
+
*/
|
|
113
|
+
projectToWorld?: boolean;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Called every time the detector produces a new set of detections.
|
|
117
|
+
* May be called with an empty array if nothing is detected in a frame.
|
|
118
|
+
*/
|
|
119
|
+
onDetection?: (event: ViroDetectionEvent) => void;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Called once the model has been loaded and the camera pipeline is running.
|
|
123
|
+
*/
|
|
124
|
+
onReady?: (event: ViroDetectorReadyEvent) => void;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Called if the model fails to load or the camera cannot be opened.
|
|
128
|
+
*/
|
|
129
|
+
onError?: (event: ViroDetectorErrorEvent) => void;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// Internal native event types
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
type NativeDetectionEvent = NativeSyntheticEvent<ViroDetectionEvent>;
|
|
137
|
+
type NativeReadyEvent = NativeSyntheticEvent<ViroDetectorReadyEvent>;
|
|
138
|
+
type NativeErrorEvent = NativeSyntheticEvent<ViroDetectorErrorEvent>;
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Component
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
const VRTObjectDetectorView = requireNativeComponent<any>("VRTObjectDetectorView");
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* ViroObjectDetector — on-device open-vocabulary object detection powered by YOLOE.
|
|
148
|
+
*
|
|
149
|
+
* Runs **only in AR**: it shares the camera feed of the enclosing
|
|
150
|
+
* `ViroARSceneNavigator` (no separate camera session, no preview of its own) and
|
|
151
|
+
* fires `onDetection` with labels, normalized bounding boxes, and an on-screen
|
|
152
|
+
* `screenBoundingBox` (dp) at up to `maxFPS`. Mount it as a child or sibling of a
|
|
153
|
+
* `ViroARSceneNavigator`; it renders nothing itself, so give it `width: 0, height: 0`.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```tsx
|
|
157
|
+
* <ViroARSceneNavigator initialScene={{ scene: MyScene }} />
|
|
158
|
+
* <ViroObjectDetector
|
|
159
|
+
* style={{ position: "absolute", width: 0, height: 0 }}
|
|
160
|
+
* mode="prompt-free"
|
|
161
|
+
* confidenceThreshold={0.4}
|
|
162
|
+
* maxFPS={15}
|
|
163
|
+
* onDetection={({ detections }) => {
|
|
164
|
+
* detections.forEach(d => console.log(d.label, d.confidence, d.screenBoundingBox));
|
|
165
|
+
* }}
|
|
166
|
+
* />
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export const ViroObjectDetector: React.FC<Props> = ({
|
|
170
|
+
model = "yoloe-26s",
|
|
171
|
+
mode = "prompt-free",
|
|
172
|
+
categories = [],
|
|
173
|
+
confidenceThreshold = 0.4,
|
|
174
|
+
iouThreshold = 0.45,
|
|
175
|
+
maxFPS = 15,
|
|
176
|
+
maxDetections = 20,
|
|
177
|
+
projectToWorld = true,
|
|
178
|
+
onDetection,
|
|
179
|
+
onReady,
|
|
180
|
+
onError,
|
|
181
|
+
style,
|
|
182
|
+
...rest
|
|
183
|
+
}) => {
|
|
184
|
+
const handleDetection = React.useCallback(
|
|
185
|
+
(event: NativeDetectionEvent) => {
|
|
186
|
+
onDetection?.(event.nativeEvent);
|
|
187
|
+
},
|
|
188
|
+
[onDetection]
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const handleReady = React.useCallback(
|
|
192
|
+
(event: NativeReadyEvent) => {
|
|
193
|
+
onReady?.(event.nativeEvent);
|
|
194
|
+
},
|
|
195
|
+
[onReady]
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const handleError = React.useCallback(
|
|
199
|
+
(event: NativeErrorEvent) => {
|
|
200
|
+
onError?.(event.nativeEvent);
|
|
201
|
+
},
|
|
202
|
+
[onError]
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<VRTObjectDetectorView
|
|
207
|
+
{...rest}
|
|
208
|
+
style={style}
|
|
209
|
+
model={model}
|
|
210
|
+
mode={mode}
|
|
211
|
+
categories={categories}
|
|
212
|
+
confidenceThreshold={confidenceThreshold}
|
|
213
|
+
iouThreshold={iouThreshold}
|
|
214
|
+
maxFPS={maxFPS}
|
|
215
|
+
maxDetections={maxDetections}
|
|
216
|
+
projectToWorld={projectToWorld}
|
|
217
|
+
onDetectionViro={onDetection ? handleDetection : undefined}
|
|
218
|
+
onReadyViro={onReady ? handleReady : undefined}
|
|
219
|
+
onErrorViro={onError ? handleError : undefined}
|
|
220
|
+
/>
|
|
221
|
+
);
|
|
222
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ViroVirtualButton.tsx
|
|
3
|
+
*
|
|
4
|
+
* Copyright © 2026 ReactVision. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
* a copy of this software and associated documentation files (the
|
|
8
|
+
* "Software"), to deal in the Software without restriction, including
|
|
9
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
* the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included
|
|
15
|
+
* in all copies or substantial portions of the Software.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
import {
|
|
20
|
+
processColor,
|
|
21
|
+
requireNativeComponent,
|
|
22
|
+
StyleProp,
|
|
23
|
+
ViewStyle,
|
|
24
|
+
} from "react-native";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Native virtual-button component. Renders an on-screen circular button labelled
|
|
28
|
+
* with the button name (e.g. "A", "B"). On press it writes setButton(idx, true)
|
|
29
|
+
* to a VROInputState in VROVirtualControllerRegistry; on release it writes false.
|
|
30
|
+
*
|
|
31
|
+
* Like ViroVirtualJoystick, touch handling is entirely native — no JS bridge
|
|
32
|
+
* round-trip — so press/release latency stays in the single-digit-milliseconds range.
|
|
33
|
+
*
|
|
34
|
+
* Example:
|
|
35
|
+
*
|
|
36
|
+
* <ViroVirtualButton
|
|
37
|
+
* controllerId="p1"
|
|
38
|
+
* button="A"
|
|
39
|
+
* style={{ position: "absolute", bottom: 60, right: 60, width: 60, height: 60 }}
|
|
40
|
+
* size={44}
|
|
41
|
+
* tintColor="rgba(255,255,255,0.6)"
|
|
42
|
+
* />
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
export type ViroButtonName =
|
|
46
|
+
| "A" | "B" | "X" | "Y" | "Z"
|
|
47
|
+
| "L1" | "R1" | "L2" | "R2"
|
|
48
|
+
| "Start" | "Select";
|
|
49
|
+
|
|
50
|
+
export interface ViroVirtualButtonProps {
|
|
51
|
+
/**
|
|
52
|
+
* Identifier of the controller this button writes to. Must match the
|
|
53
|
+
* `controllerId` of any joystick or other adapter sharing the same state.
|
|
54
|
+
*/
|
|
55
|
+
controllerId: string;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Which button this view maps to. Determines the index written to
|
|
59
|
+
* VROInputState::setButton and the label drawn on the circle.
|
|
60
|
+
*/
|
|
61
|
+
button: ViroButtonName;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Diameter of the button circle in points. Defaults to 44.
|
|
65
|
+
*/
|
|
66
|
+
size?: number;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Fill colour of the circle. Any value accepted by React Native's color prop.
|
|
70
|
+
* Defaults to `rgba(255, 255, 255, 0.6)`.
|
|
71
|
+
*/
|
|
72
|
+
tintColor?: number | string;
|
|
73
|
+
|
|
74
|
+
/** Fired when the button is pressed down (same tick as C++ VROInputState write). */
|
|
75
|
+
onPressIn?: (event: { nativeEvent: { button: string } }) => void;
|
|
76
|
+
/** Fired when the button is released (same tick as C++ VROInputState write). */
|
|
77
|
+
onPressOut?: (event: { nativeEvent: { button: string } }) => void;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Positioning / sizing.
|
|
81
|
+
*/
|
|
82
|
+
style?: StyleProp<ViewStyle>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const NativeVirtualButton =
|
|
86
|
+
requireNativeComponent<any>("VRTVirtualButtonView");
|
|
87
|
+
|
|
88
|
+
export const ViroVirtualButton: React.FC<ViroVirtualButtonProps> = (props) => {
|
|
89
|
+
const { tintColor, ...rest } = props;
|
|
90
|
+
const processedTint =
|
|
91
|
+
tintColor != null ? processColor(tintColor) : undefined;
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<NativeVirtualButton
|
|
95
|
+
{...rest}
|
|
96
|
+
tintColor={processedTint}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default ViroVirtualButton;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ViroVirtualJoystick.tsx
|
|
3
|
+
*
|
|
4
|
+
* Copyright © 2026 ReactVision. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
* a copy of this software and associated documentation files (the
|
|
8
|
+
* "Software"), to deal in the Software without restriction, including
|
|
9
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
* the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included
|
|
15
|
+
* in all copies or substantial portions of the Software.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
import {
|
|
20
|
+
Platform,
|
|
21
|
+
processColor,
|
|
22
|
+
requireNativeComponent,
|
|
23
|
+
StyleProp,
|
|
24
|
+
ViewStyle,
|
|
25
|
+
} from "react-native";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Native virtual-joystick component. Renders an on-screen analog stick whose
|
|
29
|
+
* deflection is written to a process-wide controller state identified by
|
|
30
|
+
* `controllerId`. The touch handling and state update happen entirely on the
|
|
31
|
+
* native side — events do not cross the JS bridge — so input latency stays in
|
|
32
|
+
* the single-digit-milliseconds range even under heavy JS load.
|
|
33
|
+
*
|
|
34
|
+
* Consumers read the resulting controller state from native code (via a
|
|
35
|
+
* VROFrameListener that calls VROVirtualControllerRegistry::instance().peek(id))
|
|
36
|
+
* or from JS via the forthcoming useVirtualController hook.
|
|
37
|
+
*
|
|
38
|
+
* Multiple input sources (this joystick, a future MFi-gamepad adapter, a tilt
|
|
39
|
+
* adapter) can target the same `controllerId`; the controller state aggregates
|
|
40
|
+
* whichever sources last wrote to each field.
|
|
41
|
+
*
|
|
42
|
+
* Example:
|
|
43
|
+
*
|
|
44
|
+
* <ViroVirtualJoystick
|
|
45
|
+
* controllerId="p1"
|
|
46
|
+
* stickSide="left"
|
|
47
|
+
* style={{ position: "absolute", bottom: 40, left: 40, width: 140, height: 140 }}
|
|
48
|
+
* radius={60}
|
|
49
|
+
* tintColor="rgba(255,255,255,0.6)"
|
|
50
|
+
* />
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
export type ViroStickSide = "left" | "right";
|
|
54
|
+
|
|
55
|
+
export interface ViroVirtualJoystickProps {
|
|
56
|
+
/**
|
|
57
|
+
* Identifier of the controller this joystick writes to. Matches the id used
|
|
58
|
+
* by readers (e.g. native simulation adapters) and by other input adapters
|
|
59
|
+
* targeting the same controller.
|
|
60
|
+
*/
|
|
61
|
+
controllerId: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Which stick on the controller this joystick drives. Defaults to `"left"`.
|
|
65
|
+
*/
|
|
66
|
+
stickSide?: ViroStickSide;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Outer ring radius in points (the knob is clamped to this distance from the
|
|
70
|
+
* centre). The view's frame should be at least `radius * 2 + knobMargin` on
|
|
71
|
+
* each axis for the stick to render fully inside its bounds. Defaults to 60.
|
|
72
|
+
*/
|
|
73
|
+
radius?: number;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Tint colour applied to both the outer ring stroke and the knob fill. Any
|
|
77
|
+
* value accepted by React Native's color prop. Defaults to
|
|
78
|
+
* `rgba(255, 255, 255, 0.6)`.
|
|
79
|
+
*/
|
|
80
|
+
tintColor?: number | string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fired on every stick movement with { x, y } normalised to [-1, 1].
|
|
84
|
+
* Called from native synchronously after VROInputState is updated,
|
|
85
|
+
* so you can drive JS visuals from the same user gesture.
|
|
86
|
+
*/
|
|
87
|
+
onStickChange?: (event: { nativeEvent: { x: number; y: number } }) => void;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Positioning / sizing. Same prop as any RN view.
|
|
91
|
+
*/
|
|
92
|
+
style?: StyleProp<ViewStyle>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const NativeVirtualJoystick =
|
|
96
|
+
requireNativeComponent<any>("VRTVirtualJoystickView");
|
|
97
|
+
|
|
98
|
+
export const ViroVirtualJoystick: React.FC<ViroVirtualJoystickProps> = (
|
|
99
|
+
props,
|
|
100
|
+
) => {
|
|
101
|
+
const { tintColor, onStickChange, ...rest } = props;
|
|
102
|
+
const processedTint =
|
|
103
|
+
tintColor != null ? processColor(tintColor) : undefined;
|
|
104
|
+
|
|
105
|
+
// Native sends x/y as strings to avoid Fabric conversions.h type-check spam.
|
|
106
|
+
const handleStickChange = onStickChange
|
|
107
|
+
? (e: any) => onStickChange({
|
|
108
|
+
nativeEvent: { x: parseFloat(e.nativeEvent.x), y: parseFloat(e.nativeEvent.y) }
|
|
109
|
+
})
|
|
110
|
+
: undefined;
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<NativeVirtualJoystick
|
|
114
|
+
{...rest}
|
|
115
|
+
tintColor={processedTint}
|
|
116
|
+
onStickChange={handleStickChange}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (Platform.OS === "web") {
|
|
122
|
+
// No-op on web; the joystick is a native-only view today.
|
|
123
|
+
// Tracked as a follow-up: HTML/Canvas equivalent for the wasm path.
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default ViroVirtualJoystick;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useGameLoop — convenience hook that wraps <ViroGameLoop />.
|
|
3
|
+
*
|
|
4
|
+
* NOTE: This hook does NOT imperatively mount a component. It is designed to
|
|
5
|
+
* be used alongside a <ViroGameLoop> component in the scene. The component
|
|
6
|
+
* fires the events; the hook subscribes to them via a shared ref pattern.
|
|
7
|
+
*
|
|
8
|
+
* For the most common case — one game loop per scene — use <ViroGameLoop>
|
|
9
|
+
* directly with onUpdate/onFixedUpdate props. The hook is provided as
|
|
10
|
+
* syntactic sugar for cases where the callback needs to live deep in the tree.
|
|
11
|
+
*
|
|
12
|
+
* Copyright © 2026 ReactVision. All rights reserved.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useEffect, useRef } from "react";
|
|
16
|
+
import type {
|
|
17
|
+
ViroGameLoopUpdateEvent,
|
|
18
|
+
ViroGameLoopFixedEvent,
|
|
19
|
+
} from "../ViroGameLoop";
|
|
20
|
+
|
|
21
|
+
export type UseGameLoopOptions = {
|
|
22
|
+
/** Fixed simulation frequency in Hz. Requires a ViroGameLoop with matching fixedHz. */
|
|
23
|
+
fixedHz?: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Subscribe to the variable-step update from the nearest <ViroGameLoop>.
|
|
28
|
+
*
|
|
29
|
+
* The callback is always current (uses a ref internally) so it is safe to
|
|
30
|
+
* reference component state without adding it to a dependency array.
|
|
31
|
+
*/
|
|
32
|
+
export function useGameLoop(
|
|
33
|
+
callback: (event: ViroGameLoopUpdateEvent) => void,
|
|
34
|
+
_options?: UseGameLoopOptions
|
|
35
|
+
): (event: ViroGameLoopUpdateEvent) => void {
|
|
36
|
+
const cbRef = useRef(callback);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
cbRef.current = callback;
|
|
39
|
+
});
|
|
40
|
+
// Return a stable handler to pass to ViroGameLoop's onUpdate prop
|
|
41
|
+
const handlerRef = useRef((event: ViroGameLoopUpdateEvent) => {
|
|
42
|
+
cbRef.current(event);
|
|
43
|
+
});
|
|
44
|
+
return handlerRef.current;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to the late-update (post-physics) callback.
|
|
49
|
+
*/
|
|
50
|
+
export function useLateUpdate(
|
|
51
|
+
callback: (event: ViroGameLoopUpdateEvent) => void
|
|
52
|
+
): (event: ViroGameLoopUpdateEvent) => void {
|
|
53
|
+
const cbRef = useRef(callback);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
cbRef.current = callback;
|
|
56
|
+
});
|
|
57
|
+
const handlerRef = useRef((event: ViroGameLoopUpdateEvent) => {
|
|
58
|
+
cbRef.current(event);
|
|
59
|
+
});
|
|
60
|
+
return handlerRef.current;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Subscribe to the fixed-step callback.
|
|
65
|
+
* Requires a <ViroGameLoop fixedHz={N}> in the scene.
|
|
66
|
+
*/
|
|
67
|
+
export function useFixedUpdate(
|
|
68
|
+
callback: (event: ViroGameLoopFixedEvent) => void
|
|
69
|
+
): (event: ViroGameLoopFixedEvent) => void {
|
|
70
|
+
const cbRef = useRef(callback);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
cbRef.current = callback;
|
|
73
|
+
});
|
|
74
|
+
const handlerRef = useRef((event: ViroGameLoopFixedEvent) => {
|
|
75
|
+
cbRef.current(event);
|
|
76
|
+
});
|
|
77
|
+
return handlerRef.current;
|
|
78
|
+
}
|
|
@@ -17,6 +17,14 @@ type Props = ViroCommonProps & {
|
|
|
17
17
|
onTrackingUpdated?: (state: ViroTrackingState, reason: ViroTrackingReason) => void;
|
|
18
18
|
onPlatformUpdate?: (platformInfoViro: ViroPlatformInfo) => void;
|
|
19
19
|
onAmbientLightUpdate?: (update: ViroAmbientLightInfo) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Fired once when depth data (LiDAR or monocular depth estimation) first becomes
|
|
22
|
+
* available for the session — i.e. hit tests can now return DepthPoints. Useful for
|
|
23
|
+
* hiding an "initializing depth…" state instead of acting on early, inaccurate
|
|
24
|
+
* feature points. On devices using monocular depth estimation, this can take a few
|
|
25
|
+
* seconds after the AR screen appears while the model warms up.
|
|
26
|
+
*/
|
|
27
|
+
onDepthReady?: () => void;
|
|
20
28
|
/**
|
|
21
29
|
* Describes the acoustic properties of the room around the user
|
|
22
30
|
*/
|
|
@@ -47,6 +55,10 @@ export declare class ViroARScene extends ViroBase<Props> {
|
|
|
47
55
|
* Returns object w/ "intensity" and "color" keys
|
|
48
56
|
*/
|
|
49
57
|
_onAmbientLightUpdate: (event: NativeSyntheticEvent<ViroAmbientLightUpdateEvent>) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Fired once when depth data first becomes available for hit testing.
|
|
60
|
+
*/
|
|
61
|
+
_onDepthReady: (_event: NativeSyntheticEvent<{}>) => void;
|
|
50
62
|
_onAnchorFound: (event: NativeSyntheticEvent<ViroARAnchorFoundEvent>) => void;
|
|
51
63
|
_onAnchorUpdated: (event: NativeSyntheticEvent<ViroARAnchorUpdatedEvent>) => void;
|
|
52
64
|
_onAnchorRemoved: (event: NativeSyntheticEvent<ViroARAnchorRemovedEvent>) => void;
|
|
@@ -176,6 +176,12 @@ class ViroARScene extends ViroBase_1.ViroBase {
|
|
|
176
176
|
this.props.onAmbientLightUpdate &&
|
|
177
177
|
this.props.onAmbientLightUpdate(event.nativeEvent.ambientLightInfo);
|
|
178
178
|
};
|
|
179
|
+
/**
|
|
180
|
+
* Fired once when depth data first becomes available for hit testing.
|
|
181
|
+
*/
|
|
182
|
+
_onDepthReady = (_event) => {
|
|
183
|
+
this.props.onDepthReady && this.props.onDepthReady();
|
|
184
|
+
};
|
|
179
185
|
_onAnchorFound = (event) => {
|
|
180
186
|
// TODO: this is in a different format than the other onAnchorFound methods
|
|
181
187
|
this.props.onAnchorFound &&
|
|
@@ -334,7 +340,7 @@ class ViroARScene extends ViroBase_1.ViroBase {
|
|
|
334
340
|
},
|
|
335
341
|
}}>
|
|
336
342
|
<VRTARScene {...this.props} canHover={this.props.onHover != undefined} canClick={this.props.onClick != undefined ||
|
|
337
|
-
this.props.onClickState != undefined} canTouch={this.props.onTouch != undefined} canScroll={this.props.onScroll != undefined} canSwipe={this.props.onSwipe != undefined} canDrag={this.props.onDrag != undefined} canPinch={this.props.onPinch != undefined} canRotate={this.props.onRotate != undefined} canFuse={this.props.onFuse != undefined} canCameraARHitTest={this.props.onCameraARHitTest != undefined} canARPointCloudUpdate={this.props.onARPointCloudUpdate != undefined} canCameraTransformUpdate={this.props.onCameraTransformUpdate != undefined} onHoverViro={this._onHover} onClickViro={this._onClickState} onClick={undefined} onTouchViro={this._onTouch} onScrollViro={this._onScroll} onSwipeViro={this._onSwipe} onDragViro={this._onDrag} onPinchViro={this._onPinch} onRotateViro={this._onRotate} onFuseViro={this._onFuse} onCameraARHitTestViro={this._onCameraARHitTest} onARPointCloudUpdateViro={this._onARPointCloudUpdate} onCameraTransformUpdateViro={this._onCameraTransformUpdate} onPlatformUpdateViro={this._onPlatformUpdate} onTrackingUpdatedViro={this._onTrackingUpdated} onAmbientLightUpdateViro={this._onAmbientLightUpdate} onAnchorFoundViro={this._onAnchorFound} onAnchorUpdatedViro={this._onAnchorUpdated} onAnchorRemovedViro={this._onAnchorRemoved} timeToFuse={timeToFuse} anchorDetectionTypes={anchorDetectionTypes} displayPointCloud={displayPointCloud} pointCloudImage={pointCloudImage} pointCloudScale={pointCloudScale} pointCloudMaxPoints={pointCloudMaxPoints}/>
|
|
343
|
+
this.props.onClickState != undefined} canTouch={this.props.onTouch != undefined} canScroll={this.props.onScroll != undefined} canSwipe={this.props.onSwipe != undefined} canDrag={this.props.onDrag != undefined} canPinch={this.props.onPinch != undefined} canRotate={this.props.onRotate != undefined} canFuse={this.props.onFuse != undefined} canCameraARHitTest={this.props.onCameraARHitTest != undefined} canARPointCloudUpdate={this.props.onARPointCloudUpdate != undefined} canCameraTransformUpdate={this.props.onCameraTransformUpdate != undefined} onHoverViro={this._onHover} onClickViro={this._onClickState} onClick={undefined} onTouchViro={this._onTouch} onScrollViro={this._onScroll} onSwipeViro={this._onSwipe} onDragViro={this._onDrag} onPinchViro={this._onPinch} onRotateViro={this._onRotate} onFuseViro={this._onFuse} onCameraARHitTestViro={this._onCameraARHitTest} onARPointCloudUpdateViro={this._onARPointCloudUpdate} onCameraTransformUpdateViro={this._onCameraTransformUpdate} onPlatformUpdateViro={this._onPlatformUpdate} onTrackingUpdatedViro={this._onTrackingUpdated} onAmbientLightUpdateViro={this._onAmbientLightUpdate} onDepthReadyViro={this._onDepthReady} onAnchorFoundViro={this._onAnchorFound} onAnchorUpdatedViro={this._onAnchorUpdated} onAnchorRemovedViro={this._onAnchorRemoved} timeToFuse={timeToFuse} anchorDetectionTypes={anchorDetectionTypes} displayPointCloud={displayPointCloud} pointCloudImage={pointCloudImage} pointCloudScale={pointCloudScale} pointCloudMaxPoints={pointCloudMaxPoints}/>
|
|
338
344
|
</ViroSceneContext_1.ViroSceneContext.Provider>);
|
|
339
345
|
}
|
|
340
346
|
}
|
|
@@ -368,6 +374,7 @@ ViroARScene, {
|
|
|
368
374
|
onTrackingInitializedViro: true,
|
|
369
375
|
onTrackingUpdatedViro: true,
|
|
370
376
|
onAmbientLightUpdateViro: true,
|
|
377
|
+
onDepthReadyViro: true,
|
|
371
378
|
onAnchorFoundViro: true,
|
|
372
379
|
onAnchorUpdatedViro: true,
|
|
373
380
|
onAnchorRemovedViro: true,
|
|
@@ -127,6 +127,36 @@ type Props = ViewProps & {
|
|
|
127
127
|
* @platform ios
|
|
128
128
|
*/
|
|
129
129
|
preferMonocularDepth?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Calibration scale applied to monocular depth values before use in occlusion.
|
|
132
|
+
* 1.0 (default) = no change. Use < 1.0 if the model overestimates distances
|
|
133
|
+
* (virtual objects visible through real surfaces). Use > 1.0 if it underestimates.
|
|
134
|
+
* Typical tuning range: 0.7 – 1.3.
|
|
135
|
+
*
|
|
136
|
+
* @default 1.0
|
|
137
|
+
* @platform ios
|
|
138
|
+
*/
|
|
139
|
+
monocularDepthScale?: number;
|
|
140
|
+
/**
|
|
141
|
+
* Maximum inference rate for monocular depth (default: 5).
|
|
142
|
+
* Lower values reduce device heat. Thermal state automatically
|
|
143
|
+
* overrides this downward: Fair→3fps, Serious→2fps, Critical→stopped.
|
|
144
|
+
* 3fps is barely perceptible for occlusion; 5fps is very smooth.
|
|
145
|
+
*
|
|
146
|
+
* @default 5
|
|
147
|
+
* @platform ios
|
|
148
|
+
*/
|
|
149
|
+
monocularDepthTargetFPS?: number;
|
|
150
|
+
/**
|
|
151
|
+
* Use the front (selfie) camera as the AR session background.
|
|
152
|
+
* On iOS uses ARFaceTrackingConfiguration (requires TrueDepth camera, iPhone X+).
|
|
153
|
+
* On Android uses ARCore Augmented Faces mode (front camera).
|
|
154
|
+
* World tracking, plane detection, and LiDAR are unavailable in this mode.
|
|
155
|
+
*
|
|
156
|
+
* @default false
|
|
157
|
+
* @platform ios, android
|
|
158
|
+
*/
|
|
159
|
+
frontCameraEnabled?: boolean;
|
|
130
160
|
/**
|
|
131
161
|
* Cloud and geospatial anchor provider.
|
|
132
162
|
* Set to `"reactvision"` (default) for the ReactVision backend,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamingAudioManager — imperative API for streaming PCM audio.
|
|
3
|
+
*
|
|
4
|
+
* Typical TTS flow:
|
|
5
|
+
* StreamingAudioManager.create('tts');
|
|
6
|
+
* StreamingAudioManager.beginStreaming('tts', 24000, 1);
|
|
7
|
+
* StreamingAudioManager.play('tts');
|
|
8
|
+
* // on each TTS audio chunk:
|
|
9
|
+
* StreamingAudioManager.pushSamples('tts', base64FloatPCM);
|
|
10
|
+
* // when done:
|
|
11
|
+
* StreamingAudioManager.destroy('tts');
|
|
12
|
+
*/
|
|
13
|
+
export declare const StreamingAudioManager: {
|
|
14
|
+
create: (id: string) => void;
|
|
15
|
+
beginStreaming: (id: string, sampleRate: number, channels: number) => void;
|
|
16
|
+
play: (id: string) => void;
|
|
17
|
+
pause: (id: string) => void;
|
|
18
|
+
setVolume: (id: string, volume: number) => void;
|
|
19
|
+
setMuted: (id: string, muted: boolean) => void;
|
|
20
|
+
pushSamples: (id: string, base64: string) => void;
|
|
21
|
+
destroy: (id: string) => void;
|
|
22
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StreamingAudioManager = void 0;
|
|
4
|
+
const react_native_1 = require("react-native");
|
|
5
|
+
// RCT_EXPORT_MODULE() in newer RN (0.76+) keeps the "Module" suffix in NativeModules.
|
|
6
|
+
const native = (react_native_1.NativeModules.VRTStreamingAudioModule ?? react_native_1.NativeModules.VRTStreamingAudio);
|
|
7
|
+
function noop(..._args) { }
|
|
8
|
+
/**
|
|
9
|
+
* StreamingAudioManager — imperative API for streaming PCM audio.
|
|
10
|
+
*
|
|
11
|
+
* Typical TTS flow:
|
|
12
|
+
* StreamingAudioManager.create('tts');
|
|
13
|
+
* StreamingAudioManager.beginStreaming('tts', 24000, 1);
|
|
14
|
+
* StreamingAudioManager.play('tts');
|
|
15
|
+
* // on each TTS audio chunk:
|
|
16
|
+
* StreamingAudioManager.pushSamples('tts', base64FloatPCM);
|
|
17
|
+
* // when done:
|
|
18
|
+
* StreamingAudioManager.destroy('tts');
|
|
19
|
+
*/
|
|
20
|
+
exports.StreamingAudioManager = {
|
|
21
|
+
create: native
|
|
22
|
+
? (id) => native.create(id)
|
|
23
|
+
: noop,
|
|
24
|
+
beginStreaming: native
|
|
25
|
+
? (id, sampleRate, channels) => native.beginStreaming(id, sampleRate, channels)
|
|
26
|
+
: noop,
|
|
27
|
+
play: native
|
|
28
|
+
? (id) => native.play(id)
|
|
29
|
+
: noop,
|
|
30
|
+
pause: native
|
|
31
|
+
? (id) => native.pause(id)
|
|
32
|
+
: noop,
|
|
33
|
+
setVolume: native
|
|
34
|
+
? (id, volume) => native.setVolume(id, volume)
|
|
35
|
+
: noop,
|
|
36
|
+
setMuted: native
|
|
37
|
+
? (id, muted) => native.setMuted(id, muted)
|
|
38
|
+
: noop,
|
|
39
|
+
pushSamples: native
|
|
40
|
+
? (id, base64) => native.pushSamples(id, base64)
|
|
41
|
+
: noop,
|
|
42
|
+
destroy: native
|
|
43
|
+
? (id) => native.destroy(id)
|
|
44
|
+
: noop,
|
|
45
|
+
};
|