@runhuman/sensor 0.2.2 → 0.2.3

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/dist/index.d.mts CHANGED
@@ -1,7 +1,103 @@
1
- import { TelemetryPlatform } from '@runhuman/shared';
1
+ import { CreateTelemetrySessionRequest, CreateTelemetrySessionResponse, TelemetryBatchRequest, TelemetryBatchResponse, EndTelemetrySessionResponse, TelemetrySessionStatusResponse, TelemetryPlatform } from '@runhuman/shared';
2
2
  export { TelemetryPlatform } from '@runhuman/shared';
3
- import { S as SessionManager, A as ApiClient } from './session-manager-B6tiwEQm.mjs';
4
- export { a as SessionState } from './session-manager-B6tiwEQm.mjs';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Telemetry API Client
8
+ *
9
+ * Thin HTTP client wrapping the 4 Phase 0 telemetry endpoints.
10
+ * Captures a reference to fetch at construction time — before the
11
+ * network interceptor patches globalThis.fetch — to avoid recursion.
12
+ */
13
+
14
+ declare class ApiClient {
15
+ private readonly baseUrl;
16
+ private readonly apiKey;
17
+ private readonly fetchFn;
18
+ constructor(baseUrl: string, apiKey: string);
19
+ createSession(req: CreateTelemetrySessionRequest): Promise<CreateTelemetrySessionResponse>;
20
+ submitBatch(sessionId: string, req: TelemetryBatchRequest): Promise<TelemetryBatchResponse>;
21
+ endSession(sessionId: string): Promise<EndTelemetrySessionResponse>;
22
+ getSessionStatus(jobId: string): Promise<TelemetrySessionStatusResponse>;
23
+ resolveShortCode(code: string): Promise<{
24
+ jobId: string;
25
+ } | null>;
26
+ private url;
27
+ private post;
28
+ private get;
29
+ }
30
+
31
+ /**
32
+ * Session Manager
33
+ *
34
+ * Orchestrates the sensor lifecycle:
35
+ * idle → polling → active → ending → idle
36
+ *
37
+ * - Polls the status endpoint to detect when a tester starts a job
38
+ * - Creates a telemetry session and installs interceptors
39
+ * - Periodically flushes buffered events to the API
40
+ * - Ends the session when the job is no longer active
41
+ */
42
+
43
+ type SessionState = 'idle' | 'polling' | 'active' | 'ending';
44
+ interface SessionManagerConfig {
45
+ apiClient: ApiClient;
46
+ platform: TelemetryPlatform;
47
+ sdkVersion: string;
48
+ flushIntervalMs: number;
49
+ pollIntervalMs: number;
50
+ maxBufferSize: number;
51
+ debug: boolean;
52
+ }
53
+ declare class SessionManager {
54
+ private state;
55
+ private sessionId;
56
+ private activeJobId;
57
+ private pollTimer;
58
+ private flushTimer;
59
+ private readonly buffer;
60
+ private readonly interceptors;
61
+ private readonly config;
62
+ private readonly listeners;
63
+ constructor(config: SessionManagerConfig);
64
+ getState(): SessionState;
65
+ /** Alias for getState() — named for React's useSyncExternalStore convention */
66
+ getSnapshot(): SessionState;
67
+ getSessionId(): string | null;
68
+ getActiveJobId(): string | null;
69
+ /**
70
+ * Subscribe to state changes. Returns an unsubscribe function.
71
+ * Compatible with React's useSyncExternalStore.
72
+ */
73
+ subscribe(listener: () => void): () => void;
74
+ private setState;
75
+ /**
76
+ * Start polling for job activation.
77
+ * The sensor calls this on init — polling continues until a job is detected
78
+ * or stopPolling() is called.
79
+ */
80
+ startPolling(jobId: string): void;
81
+ stopPolling(): void;
82
+ /**
83
+ * Activate a session immediately (e.g., from a deep link).
84
+ * Skips polling — goes straight to session creation.
85
+ */
86
+ activate(jobId: string): Promise<void>;
87
+ /**
88
+ * End the current session and return to idle.
89
+ */
90
+ deactivate(): Promise<void>;
91
+ /**
92
+ * Full teardown — stop everything and clean up.
93
+ */
94
+ destroy(): Promise<void>;
95
+ private pollOnce;
96
+ private startSession;
97
+ private endSession;
98
+ private flush;
99
+ private log;
100
+ }
5
101
 
6
102
  /**
7
103
  * Runhuman Sensor — Public API
@@ -79,4 +175,33 @@ declare class Runhuman {
79
175
  private log;
80
176
  }
81
177
 
82
- export { ApiClient, Runhuman, type RunhumanConfig };
178
+ type OverlayPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
179
+ interface RunhumanOverlayProps {
180
+ children: React.ReactNode;
181
+ /** Corner for the overlay UI (default: 'bottom-right') */
182
+ position?: OverlayPosition;
183
+ }
184
+ declare function RunhumanOverlay({ children, position }: RunhumanOverlayProps): react_jsx_runtime.JSX.Element;
185
+
186
+ /**
187
+ * React hook for subscribing to sensor state changes.
188
+ * Uses useSyncExternalStore for tear-safe subscriptions.
189
+ *
190
+ * Handles the case where Runhuman.init() hasn't been called yet
191
+ * (e.g., during first render before useEffect fires) by polling
192
+ * until the instance is available.
193
+ */
194
+
195
+ interface SensorState {
196
+ state: SessionState;
197
+ activeJobId: string | null;
198
+ sessionId: string | null;
199
+ }
200
+ /**
201
+ * Subscribe to the sensor's session state.
202
+ * Safe to call before Runhuman.init() — returns idle state and
203
+ * polls every 100ms until the instance becomes available.
204
+ */
205
+ declare function useSensorState(): SensorState;
206
+
207
+ export { ApiClient, type OverlayPosition, Runhuman, type RunhumanConfig, RunhumanOverlay, type RunhumanOverlayProps, type SensorState, type SessionState, useSensorState };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,103 @@
1
- import { TelemetryPlatform } from '@runhuman/shared';
1
+ import { CreateTelemetrySessionRequest, CreateTelemetrySessionResponse, TelemetryBatchRequest, TelemetryBatchResponse, EndTelemetrySessionResponse, TelemetrySessionStatusResponse, TelemetryPlatform } from '@runhuman/shared';
2
2
  export { TelemetryPlatform } from '@runhuman/shared';
3
- import { S as SessionManager, A as ApiClient } from './session-manager-B6tiwEQm.js';
4
- export { a as SessionState } from './session-manager-B6tiwEQm.js';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import React from 'react';
5
+
6
+ /**
7
+ * Telemetry API Client
8
+ *
9
+ * Thin HTTP client wrapping the 4 Phase 0 telemetry endpoints.
10
+ * Captures a reference to fetch at construction time — before the
11
+ * network interceptor patches globalThis.fetch — to avoid recursion.
12
+ */
13
+
14
+ declare class ApiClient {
15
+ private readonly baseUrl;
16
+ private readonly apiKey;
17
+ private readonly fetchFn;
18
+ constructor(baseUrl: string, apiKey: string);
19
+ createSession(req: CreateTelemetrySessionRequest): Promise<CreateTelemetrySessionResponse>;
20
+ submitBatch(sessionId: string, req: TelemetryBatchRequest): Promise<TelemetryBatchResponse>;
21
+ endSession(sessionId: string): Promise<EndTelemetrySessionResponse>;
22
+ getSessionStatus(jobId: string): Promise<TelemetrySessionStatusResponse>;
23
+ resolveShortCode(code: string): Promise<{
24
+ jobId: string;
25
+ } | null>;
26
+ private url;
27
+ private post;
28
+ private get;
29
+ }
30
+
31
+ /**
32
+ * Session Manager
33
+ *
34
+ * Orchestrates the sensor lifecycle:
35
+ * idle → polling → active → ending → idle
36
+ *
37
+ * - Polls the status endpoint to detect when a tester starts a job
38
+ * - Creates a telemetry session and installs interceptors
39
+ * - Periodically flushes buffered events to the API
40
+ * - Ends the session when the job is no longer active
41
+ */
42
+
43
+ type SessionState = 'idle' | 'polling' | 'active' | 'ending';
44
+ interface SessionManagerConfig {
45
+ apiClient: ApiClient;
46
+ platform: TelemetryPlatform;
47
+ sdkVersion: string;
48
+ flushIntervalMs: number;
49
+ pollIntervalMs: number;
50
+ maxBufferSize: number;
51
+ debug: boolean;
52
+ }
53
+ declare class SessionManager {
54
+ private state;
55
+ private sessionId;
56
+ private activeJobId;
57
+ private pollTimer;
58
+ private flushTimer;
59
+ private readonly buffer;
60
+ private readonly interceptors;
61
+ private readonly config;
62
+ private readonly listeners;
63
+ constructor(config: SessionManagerConfig);
64
+ getState(): SessionState;
65
+ /** Alias for getState() — named for React's useSyncExternalStore convention */
66
+ getSnapshot(): SessionState;
67
+ getSessionId(): string | null;
68
+ getActiveJobId(): string | null;
69
+ /**
70
+ * Subscribe to state changes. Returns an unsubscribe function.
71
+ * Compatible with React's useSyncExternalStore.
72
+ */
73
+ subscribe(listener: () => void): () => void;
74
+ private setState;
75
+ /**
76
+ * Start polling for job activation.
77
+ * The sensor calls this on init — polling continues until a job is detected
78
+ * or stopPolling() is called.
79
+ */
80
+ startPolling(jobId: string): void;
81
+ stopPolling(): void;
82
+ /**
83
+ * Activate a session immediately (e.g., from a deep link).
84
+ * Skips polling — goes straight to session creation.
85
+ */
86
+ activate(jobId: string): Promise<void>;
87
+ /**
88
+ * End the current session and return to idle.
89
+ */
90
+ deactivate(): Promise<void>;
91
+ /**
92
+ * Full teardown — stop everything and clean up.
93
+ */
94
+ destroy(): Promise<void>;
95
+ private pollOnce;
96
+ private startSession;
97
+ private endSession;
98
+ private flush;
99
+ private log;
100
+ }
5
101
 
6
102
  /**
7
103
  * Runhuman Sensor — Public API
@@ -79,4 +175,33 @@ declare class Runhuman {
79
175
  private log;
80
176
  }
81
177
 
82
- export { ApiClient, Runhuman, type RunhumanConfig };
178
+ type OverlayPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
179
+ interface RunhumanOverlayProps {
180
+ children: React.ReactNode;
181
+ /** Corner for the overlay UI (default: 'bottom-right') */
182
+ position?: OverlayPosition;
183
+ }
184
+ declare function RunhumanOverlay({ children, position }: RunhumanOverlayProps): react_jsx_runtime.JSX.Element;
185
+
186
+ /**
187
+ * React hook for subscribing to sensor state changes.
188
+ * Uses useSyncExternalStore for tear-safe subscriptions.
189
+ *
190
+ * Handles the case where Runhuman.init() hasn't been called yet
191
+ * (e.g., during first render before useEffect fires) by polling
192
+ * until the instance is available.
193
+ */
194
+
195
+ interface SensorState {
196
+ state: SessionState;
197
+ activeJobId: string | null;
198
+ sessionId: string | null;
199
+ }
200
+ /**
201
+ * Subscribe to the sensor's session state.
202
+ * Safe to call before Runhuman.init() — returns idle state and
203
+ * polls every 100ms until the instance becomes available.
204
+ */
205
+ declare function useSensorState(): SensorState;
206
+
207
+ export { ApiClient, type OverlayPosition, Runhuman, type RunhumanConfig, RunhumanOverlay, type RunhumanOverlayProps, type SensorState, type SessionState, useSensorState };
package/dist/index.js CHANGED
@@ -20,7 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- Runhuman: () => Runhuman
23
+ Runhuman: () => Runhuman,
24
+ RunhumanOverlay: () => RunhumanOverlay,
25
+ useSensorState: () => useSensorState
24
26
  });
25
27
  module.exports = __toCommonJS(index_exports);
26
28
 
@@ -1173,8 +1175,277 @@ var Runhuman = class _Runhuman {
1173
1175
  }
1174
1176
  }
1175
1177
  };
1178
+
1179
+ // src/overlay/RunhumanOverlay.tsx
1180
+ var import_react_native4 = require("react-native");
1181
+
1182
+ // src/overlay/use-sensor-state.ts
1183
+ var import_react = require("react");
1184
+ var import_react2 = require("react");
1185
+ var IDLE_STATE = { state: "idle", activeJobId: null, sessionId: null };
1186
+ function tryGetInstance() {
1187
+ try {
1188
+ return Runhuman.getInstance();
1189
+ } catch {
1190
+ return null;
1191
+ }
1192
+ }
1193
+ function useSensorState() {
1194
+ const subscribe = (0, import_react.useCallback)((onStoreChange) => {
1195
+ const instance = tryGetInstance();
1196
+ if (!instance) {
1197
+ const interval = setInterval(() => {
1198
+ if (tryGetInstance()) {
1199
+ clearInterval(interval);
1200
+ onStoreChange();
1201
+ }
1202
+ }, 100);
1203
+ return () => clearInterval(interval);
1204
+ }
1205
+ return instance.getSessionManager().subscribe(onStoreChange);
1206
+ }, []);
1207
+ const getSnapshot = (0, import_react.useCallback)(() => {
1208
+ const instance = tryGetInstance();
1209
+ if (!instance) return IDLE_STATE;
1210
+ const sm = instance.getSessionManager();
1211
+ return {
1212
+ state: sm.getSnapshot(),
1213
+ activeJobId: sm.getActiveJobId(),
1214
+ sessionId: sm.getSessionId()
1215
+ };
1216
+ }, []);
1217
+ return (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
1218
+ }
1219
+
1220
+ // src/overlay/CodeEntryPanel.tsx
1221
+ var import_react3 = require("react");
1222
+ var import_react_native2 = require("react-native");
1223
+
1224
+ // src/overlay/overlay-styles.ts
1225
+ var import_react_native = require("react-native");
1226
+ var BRAND_GREEN = "#22c55e";
1227
+ var BRAND_AMBER = "#f59e0b";
1228
+ var BRAND_RED = "#ef4444";
1229
+ var BRAND_BLUE = "#3b82f6";
1230
+ var OVERLAY_BG = "rgba(0, 0, 0, 0.85)";
1231
+ var OVERLAY_BORDER = "rgba(255, 255, 255, 0.15)";
1232
+ var overlayStyles = import_react_native.StyleSheet.create({
1233
+ // Positioning containers
1234
+ topLeft: { top: 60, left: 16 },
1235
+ topRight: { top: 60, right: 16 },
1236
+ bottomLeft: { bottom: 40, left: 16 },
1237
+ bottomRight: { bottom: 40, right: 16 },
1238
+ // Code entry panel
1239
+ panel: {
1240
+ position: "absolute",
1241
+ zIndex: 99999,
1242
+ backgroundColor: OVERLAY_BG,
1243
+ borderRadius: 12,
1244
+ borderWidth: 1,
1245
+ borderColor: OVERLAY_BORDER,
1246
+ padding: 16,
1247
+ width: 220
1248
+ },
1249
+ panelTitle: {
1250
+ color: "#ffffff",
1251
+ fontSize: 13,
1252
+ fontWeight: "600",
1253
+ marginBottom: 8
1254
+ },
1255
+ input: {
1256
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
1257
+ borderRadius: 8,
1258
+ borderWidth: 1,
1259
+ borderColor: OVERLAY_BORDER,
1260
+ color: "#ffffff",
1261
+ fontSize: 18,
1262
+ fontFamily: "monospace",
1263
+ fontWeight: "bold",
1264
+ letterSpacing: 2,
1265
+ padding: 10,
1266
+ textAlign: "center"
1267
+ },
1268
+ inputError: {
1269
+ borderColor: BRAND_RED
1270
+ },
1271
+ submitButton: {
1272
+ backgroundColor: BRAND_BLUE,
1273
+ borderRadius: 8,
1274
+ paddingVertical: 8,
1275
+ marginTop: 8,
1276
+ alignItems: "center"
1277
+ },
1278
+ submitButtonDisabled: {
1279
+ opacity: 0.5
1280
+ },
1281
+ submitButtonText: {
1282
+ color: "#ffffff",
1283
+ fontSize: 13,
1284
+ fontWeight: "600"
1285
+ },
1286
+ errorText: {
1287
+ color: BRAND_RED,
1288
+ fontSize: 11,
1289
+ marginTop: 4,
1290
+ textAlign: "center"
1291
+ },
1292
+ minimizeButton: {
1293
+ position: "absolute",
1294
+ top: 8,
1295
+ right: 8
1296
+ },
1297
+ minimizeText: {
1298
+ color: "rgba(255, 255, 255, 0.5)",
1299
+ fontSize: 16
1300
+ },
1301
+ // Minimized fab (floating action button)
1302
+ fab: {
1303
+ position: "absolute",
1304
+ zIndex: 99999,
1305
+ width: 40,
1306
+ height: 40,
1307
+ borderRadius: 20,
1308
+ backgroundColor: OVERLAY_BG,
1309
+ borderWidth: 1,
1310
+ borderColor: OVERLAY_BORDER,
1311
+ alignItems: "center",
1312
+ justifyContent: "center"
1313
+ },
1314
+ fabText: {
1315
+ color: "#ffffff",
1316
+ fontSize: 16
1317
+ },
1318
+ // Active indicator dot
1319
+ indicator: {
1320
+ position: "absolute",
1321
+ zIndex: 99999,
1322
+ width: 12,
1323
+ height: 12,
1324
+ borderRadius: 6
1325
+ },
1326
+ indicatorActive: {
1327
+ backgroundColor: BRAND_GREEN
1328
+ },
1329
+ indicatorEnding: {
1330
+ backgroundColor: BRAND_AMBER
1331
+ },
1332
+ indicatorPolling: {
1333
+ backgroundColor: BRAND_BLUE
1334
+ }
1335
+ });
1336
+
1337
+ // src/overlay/CodeEntryPanel.tsx
1338
+ var import_jsx_runtime = require("react/jsx-runtime");
1339
+ function CodeEntryPanel({ position }) {
1340
+ const [code, setCode] = (0, import_react3.useState)("");
1341
+ const [error, setError] = (0, import_react3.useState)(null);
1342
+ const [resolving, setResolving] = (0, import_react3.useState)(false);
1343
+ const [minimized, setMinimized] = (0, import_react3.useState)(false);
1344
+ const handleSubmit = async () => {
1345
+ const trimmed = code.trim();
1346
+ if (!trimmed) return;
1347
+ setResolving(true);
1348
+ setError(null);
1349
+ const instance = Runhuman.getInstance();
1350
+ const result = await instance.getApiClient().resolveShortCode(trimmed);
1351
+ if (result) {
1352
+ instance.activate(result.jobId);
1353
+ } else {
1354
+ setError("Invalid or expired code");
1355
+ setResolving(false);
1356
+ }
1357
+ };
1358
+ if (minimized) {
1359
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Pressable, { style: [overlayStyles.fab, overlayStyles[position]], onPress: () => setMinimized(false), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.fabText, children: "RH" }) });
1360
+ }
1361
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: [overlayStyles.panel, overlayStyles[position]], children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Pressable, { style: overlayStyles.minimizeButton, onPress: () => setMinimized(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.minimizeText, children: "-" }) }),
1363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.panelTitle, children: "Runhuman Sensor" }),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1365
+ import_react_native2.TextInput,
1366
+ {
1367
+ style: error ? { ...overlayStyles.input, ...overlayStyles.inputError } : overlayStyles.input,
1368
+ value: code,
1369
+ onChangeText: (text) => {
1370
+ setCode(text.toUpperCase());
1371
+ setError(null);
1372
+ },
1373
+ placeholder: "RH-XXXX",
1374
+ placeholderTextColor: "rgba(255,255,255,0.3)",
1375
+ autoCapitalize: "characters",
1376
+ maxLength: 10,
1377
+ editable: !resolving,
1378
+ onSubmitEditing: handleSubmit
1379
+ }
1380
+ ),
1381
+ error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.errorText, children: error }) : null,
1382
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1383
+ import_react_native2.Pressable,
1384
+ {
1385
+ style: resolving ? { ...overlayStyles.submitButton, ...overlayStyles.submitButtonDisabled } : overlayStyles.submitButton,
1386
+ onPress: handleSubmit,
1387
+ disabled: resolving || !code.trim(),
1388
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: overlayStyles.submitButtonText, children: resolving ? "Activating..." : "Activate" })
1389
+ }
1390
+ )
1391
+ ] });
1392
+ }
1393
+
1394
+ // src/overlay/ActiveIndicator.tsx
1395
+ var import_react4 = require("react");
1396
+ var import_react_native3 = require("react-native");
1397
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1398
+ var stateStyle = {
1399
+ active: overlayStyles.indicatorActive,
1400
+ ending: overlayStyles.indicatorEnding,
1401
+ polling: overlayStyles.indicatorPolling
1402
+ };
1403
+ function ActiveIndicator({ state, position }) {
1404
+ const pulse = (0, import_react4.useRef)(new import_react_native3.Animated.Value(1)).current;
1405
+ (0, import_react4.useEffect)(() => {
1406
+ if (state === "active") {
1407
+ const animation = import_react_native3.Animated.loop(
1408
+ import_react_native3.Animated.sequence([
1409
+ import_react_native3.Animated.timing(pulse, { toValue: 0.3, duration: 800, useNativeDriver: true }),
1410
+ import_react_native3.Animated.timing(pulse, { toValue: 1, duration: 800, useNativeDriver: true })
1411
+ ])
1412
+ );
1413
+ animation.start();
1414
+ return () => animation.stop();
1415
+ }
1416
+ pulse.setValue(1);
1417
+ }, [state, pulse]);
1418
+ const colorStyle = stateStyle[state];
1419
+ if (!colorStyle) return null;
1420
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Animated.View, { style: [overlayStyles.indicator, overlayStyles[position], colorStyle, { opacity: pulse }] });
1421
+ }
1422
+
1423
+ // src/overlay/RunhumanOverlay.tsx
1424
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1425
+ var positionMap = {
1426
+ "top-left": "topLeft",
1427
+ "top-right": "topRight",
1428
+ "bottom-left": "bottomLeft",
1429
+ "bottom-right": "bottomRight"
1430
+ };
1431
+ function RunhumanOverlay({ children, position = "bottom-right" }) {
1432
+ const { state, activeJobId } = useSensorState();
1433
+ const posKey = positionMap[position];
1434
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_native4.View, { style: styles.container, children: [
1435
+ children,
1436
+ state === "idle" && !activeJobId ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CodeEntryPanel, { position: posKey }) : null,
1437
+ state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActiveIndicator, { state, position: posKey }) : null
1438
+ ] });
1439
+ }
1440
+ var styles = import_react_native4.StyleSheet.create({
1441
+ container: {
1442
+ flex: 1
1443
+ }
1444
+ });
1176
1445
  // Annotate the CommonJS export names for ESM import in node:
1177
1446
  0 && (module.exports = {
1178
- Runhuman
1447
+ Runhuman,
1448
+ RunhumanOverlay,
1449
+ useSensorState
1179
1450
  });
1180
1451
  //# sourceMappingURL=index.js.map