@runhuman/sensor 0.2.1 → 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 +131 -5
- package/dist/index.d.ts +131 -5
- package/dist/index.js +281 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +278 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -7
- package/dist/overlay/index.d.mts +0 -34
- package/dist/overlay/index.d.ts +0 -34
- package/dist/overlay/index.js +0 -1445
- package/dist/overlay/index.js.map +0 -1
- package/dist/overlay/index.mjs +0 -1424
- 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
|
|
@@ -36,7 +132,8 @@ interface RunhumanConfig {
|
|
|
36
132
|
debug?: boolean;
|
|
37
133
|
}
|
|
38
134
|
declare class Runhuman {
|
|
39
|
-
private static instance;
|
|
135
|
+
private static get instance();
|
|
136
|
+
private static set instance(value);
|
|
40
137
|
private readonly apiClient;
|
|
41
138
|
private readonly sessionManager;
|
|
42
139
|
private readonly deepLinkHandler;
|
|
@@ -78,4 +175,33 @@ declare class Runhuman {
|
|
|
78
175
|
private log;
|
|
79
176
|
}
|
|
80
177
|
|
|
81
|
-
|
|
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
|
|
@@ -36,7 +132,8 @@ interface RunhumanConfig {
|
|
|
36
132
|
debug?: boolean;
|
|
37
133
|
}
|
|
38
134
|
declare class Runhuman {
|
|
39
|
-
private static instance;
|
|
135
|
+
private static get instance();
|
|
136
|
+
private static set instance(value);
|
|
40
137
|
private readonly apiClient;
|
|
41
138
|
private readonly sessionManager;
|
|
42
139
|
private readonly deepLinkHandler;
|
|
@@ -78,4 +175,33 @@ declare class Runhuman {
|
|
|
78
175
|
private log;
|
|
79
176
|
}
|
|
80
177
|
|
|
81
|
-
|
|
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
|
|
|
@@ -1073,7 +1075,14 @@ var DeepLinkHandler = class {
|
|
|
1073
1075
|
// src/runhuman.ts
|
|
1074
1076
|
var PRODUCTION_BASE_URL = "https://qa-experiment.fly.dev";
|
|
1075
1077
|
var SDK_VERSION = "0.1.0";
|
|
1076
|
-
var
|
|
1078
|
+
var INSTANCE_KEY = "__runhuman_sensor_instance__";
|
|
1079
|
+
var Runhuman = class _Runhuman {
|
|
1080
|
+
static get instance() {
|
|
1081
|
+
return globalThis[INSTANCE_KEY] ?? null;
|
|
1082
|
+
}
|
|
1083
|
+
static set instance(value) {
|
|
1084
|
+
globalThis[INSTANCE_KEY] = value;
|
|
1085
|
+
}
|
|
1077
1086
|
constructor(config) {
|
|
1078
1087
|
this.debug = config.debug === true;
|
|
1079
1088
|
this.apiClient = new ApiClient(
|
|
@@ -1166,10 +1175,277 @@ var _Runhuman = class _Runhuman {
|
|
|
1166
1175
|
}
|
|
1167
1176
|
}
|
|
1168
1177
|
};
|
|
1169
|
-
|
|
1170
|
-
|
|
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
|
+
});
|
|
1171
1445
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1172
1446
|
0 && (module.exports = {
|
|
1173
|
-
Runhuman
|
|
1447
|
+
Runhuman,
|
|
1448
|
+
RunhumanOverlay,
|
|
1449
|
+
useSensorState
|
|
1174
1450
|
});
|
|
1175
1451
|
//# sourceMappingURL=index.js.map
|