@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 +129 -4
- package/dist/index.d.ts +129 -4
- package/dist/index.js +273 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +270 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -7
- package/dist/overlay/index.d.mts +0 -30
- package/dist/overlay/index.d.ts +0 -30
- package/dist/overlay/index.js +0 -1431
- package/dist/overlay/index.js.map +0 -1
- package/dist/overlay/index.mjs +0 -1410
- package/dist/overlay/index.mjs.map +0 -1
- package/dist/session-manager-B6tiwEQm.d.mts +0 -99
- package/dist/session-manager-B6tiwEQm.d.ts +0 -99
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
|
|
4
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
|
|
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
|
-
|
|
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
|