@rexai/pulse-react 1.0.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 ADDED
@@ -0,0 +1,158 @@
1
+ # @rexai/pulse-react
2
+
3
+ React SDK for PulseAI - measure vital signs from any camera using remote photoplethysmography (rPPG).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rexai/pulse-react
9
+ # or
10
+ yarn add @rexai/pulse-react
11
+ # or
12
+ pnpm add @rexai/pulse-react
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```tsx
18
+ import { PulseScanner, VitalsCard } from '@rexai/pulse-react';
19
+ import '@rexai/pulse-react/styles.css';
20
+ import { useState } from 'react';
21
+
22
+ function App() {
23
+ const [vitals, setVitals] = useState(null);
24
+
25
+ return (
26
+ <div>
27
+ <PulseScanner
28
+ apiKey="pk_live_YOUR_API_KEY"
29
+ duration={10}
30
+ showMesh={true} // Show face mesh overlay
31
+ onResult={(result) => setVitals(result)}
32
+ onError={(err) => console.error(err)}
33
+ />
34
+
35
+ {vitals && (
36
+ <VitalsCard
37
+ heartRate={vitals.heart_rate}
38
+ spo2={vitals.spo2}
39
+ stressIndex={vitals.stress_index}
40
+ hrvSdnn={vitals.hrv_sdnn}
41
+ />
42
+ )}
43
+ </div>
44
+ );
45
+ }
46
+ ```
47
+
48
+ **Note:** The SDK automatically uses your `VITE_API_URL` environment variable. No need to pass `apiUrl` if it's already configured.
49
+
50
+ ## Components
51
+
52
+ ### PulseScanner
53
+
54
+ The main component for capturing and analyzing vital signs.
55
+
56
+ ```tsx
57
+ <PulseScanner
58
+ apiKey="pk_live_..." // Required: Your API key
59
+ duration={10} // Scan duration in seconds (default: 10)
60
+ onResult={(vitals) => {...}} // Called with results
61
+ onError={(error) => {...}} // Called on error
62
+ theme="light" // "light" | "dark" | "minimal"
63
+ showPreview={true} // Show camera preview
64
+ showOverlay={true} // Show face detection overlay
65
+ showMesh={true} // Show face mesh lines (default: false)
66
+ showCountdown={true} // Show countdown during recording
67
+ userData={{ // Optional user data for calibration
68
+ age: 30,
69
+ sex: 'male'
70
+ }}
71
+ />
72
+ ```
73
+
74
+ ### VitalsCard
75
+
76
+ Display component for vital sign results.
77
+
78
+ ```tsx
79
+ <VitalsCard
80
+ heartRate={72}
81
+ spo2={98}
82
+ stressIndex={25}
83
+ hrvSdnn={45}
84
+ respiratoryRate={16}
85
+ bloodPressure={{ systolic: 120, diastolic: 80 }}
86
+ recoveryScore={85}
87
+ showAll={true} // Show extended metrics
88
+ compact={false} // Compact layout
89
+ theme="light" // "light" | "dark"
90
+ />
91
+ ```
92
+
93
+ ## API Client
94
+
95
+ For programmatic access without the UI components:
96
+
97
+ ```typescript
98
+ import { PulseClient } from '@rexai/pulse-react';
99
+
100
+ const client = new PulseClient({
101
+ apiKey: 'pk_live_YOUR_API_KEY'
102
+ });
103
+
104
+ // Analyze a video blob
105
+ const vitals = await client.analyze({
106
+ video: videoBlob,
107
+ age: 30,
108
+ sex: 'male'
109
+ });
110
+
111
+ console.log(vitals.heart_rate); // 72
112
+ console.log(vitals.spo2); // 98
113
+ ```
114
+
115
+ ## Hooks
116
+
117
+ For building custom UI:
118
+
119
+ ```tsx
120
+ import { useCamera, useFaceDetection, useRecording } from '@rexai/pulse-react';
121
+
122
+ function CustomScanner() {
123
+ const { videoRef, isActive, start, stop } = useCamera();
124
+ const { detected, qualityMessage, isQualityGood } = useFaceDetection(videoRef.current);
125
+ const { isRecording, timeLeft, blob, start: startRecording } = useRecording(stream);
126
+
127
+ // Build your own UI...
128
+ }
129
+ ```
130
+
131
+ ## Vitals Returned
132
+
133
+ | Metric | Type | Description |
134
+ |--------|------|-------------|
135
+ | `heart_rate` | number | Heart rate in BPM |
136
+ | `spo2` | number | Oxygen saturation % |
137
+ | `hrv_sdnn` | number | HRV (SDNN) in ms |
138
+ | `hrv_rmssd` | number | HRV (RMSSD) in ms |
139
+ | `stress_index` | number | Stress index (0-100) |
140
+ | `respiratory_rate` | number | Breaths per minute |
141
+ | `blood_pressure` | object | Estimated BP (systolic/diastolic) |
142
+ | `recovery_score` | number | Recovery score % |
143
+ | `vascular_age` | number | Estimated vascular age |
144
+
145
+ ## Getting an API Key
146
+
147
+ 1. Visit [pulseai.com/developer/login](https://pulseai.com/developer/login)
148
+ 2. Sign in with Google
149
+ 3. Generate an API key from the dashboard
150
+
151
+ ## Support
152
+
153
+ - Documentation: [pulseai.com/docs](https://pulseai.com/docs)
154
+ - Email: support@pulseai.com
155
+
156
+ ## License
157
+
158
+ MIT
@@ -0,0 +1,432 @@
1
+ import React$1 from 'react';
2
+
3
+ /**
4
+ * PulseAI SDK Type Definitions
5
+ */
6
+ interface BloodPressure {
7
+ systolic: number;
8
+ diastolic: number;
9
+ }
10
+ interface ArrhythmiaResult {
11
+ has_arrhythmia: boolean;
12
+ irregularity_score: number;
13
+ details: string;
14
+ }
15
+ interface VisualAnalysis {
16
+ jaundice_risk: 'Low' | 'Medium' | 'High';
17
+ anemia_risk: 'Low' | 'Medium' | 'High';
18
+ bilirubin_proxy: number;
19
+ pallor_index: number;
20
+ hydration_level?: string;
21
+ hydration_score?: number;
22
+ fatigue_indicators?: {
23
+ eye_redness: number;
24
+ under_eye_darkness: number;
25
+ puffiness: number;
26
+ overall_fatigue: string;
27
+ };
28
+ skin_health?: {
29
+ evenness: number;
30
+ redness: number;
31
+ oiliness_proxy: number;
32
+ };
33
+ }
34
+ interface MentalWellness {
35
+ wellness_score: number;
36
+ level: string;
37
+ emotion: string;
38
+ emotion_confidence?: number;
39
+ blink_rate_per_min: number;
40
+ gaze_stability: number;
41
+ gaze_direction?: string;
42
+ components: {
43
+ blink_score: number;
44
+ gaze_score: number;
45
+ emotion_score: number;
46
+ hrv_score: number;
47
+ };
48
+ }
49
+ interface CompositeScores {
50
+ cardio_score: number;
51
+ resilience_score: number;
52
+ metabolic_score: number;
53
+ vitality_score: number;
54
+ }
55
+ interface BMIEstimate {
56
+ value: number;
57
+ category: string;
58
+ fwhr: number;
59
+ confidence?: string;
60
+ method?: string;
61
+ }
62
+ interface VitalsResult {
63
+ heart_rate: number;
64
+ respiratory_rate: number;
65
+ hrv_sdnn: number;
66
+ hrv_rmssd: number;
67
+ recovery_score: number;
68
+ stress_index: number;
69
+ arrhythmia: ArrhythmiaResult;
70
+ rsa_amplitude: number;
71
+ blood_pressure: BloodPressure;
72
+ spo2: number;
73
+ signal_snippet: number[];
74
+ vascular_age: number;
75
+ bmi_est: BMIEstimate;
76
+ manual_bmi?: number;
77
+ metabolic_score: number;
78
+ skin_temperature: number;
79
+ composite_scores: CompositeScores;
80
+ visual_analysis?: VisualAnalysis;
81
+ mental_wellness?: MentalWellness;
82
+ fusion_weights: Record<string, number>;
83
+ }
84
+ interface BeautySummary {
85
+ overall_score: number;
86
+ skin_age: number;
87
+ }
88
+ interface BeautySurface {
89
+ texture: number;
90
+ hydration: number;
91
+ foundation_hex: string;
92
+ undertone: string;
93
+ acne_count: number;
94
+ acne_severity: string;
95
+ wrinkle_severity: string;
96
+ wrinkle_density: number;
97
+ oiliness_index: number;
98
+ matches: string;
99
+ skin_health: {
100
+ evenness: number;
101
+ redness: number;
102
+ oiliness_proxy: number;
103
+ };
104
+ }
105
+ interface BeautyGeometry {
106
+ symmetry_score: number;
107
+ face_shape: string;
108
+ eye_openness: number;
109
+ ptosis_indices: {
110
+ status: string;
111
+ youth_ratio?: number;
112
+ };
113
+ }
114
+ interface BeautyPhysiology {
115
+ glow_index: number;
116
+ stress_level: string;
117
+ elasticity: number;
118
+ }
119
+ interface BeautyRecommendations {
120
+ routine: string[];
121
+ tips: string[];
122
+ }
123
+ interface BeautyResult {
124
+ summary: BeautySummary;
125
+ surface: BeautySurface;
126
+ geometry: BeautyGeometry;
127
+ physiology: BeautyPhysiology;
128
+ recommendations: BeautyRecommendations;
129
+ }
130
+ interface APIResponse<T> {
131
+ status: 'success' | 'error';
132
+ data: T;
133
+ meta?: {
134
+ privacy: string;
135
+ };
136
+ message?: string;
137
+ }
138
+ interface APIError {
139
+ detail: string;
140
+ status_code?: number;
141
+ }
142
+
143
+ /**
144
+ * Props for PulseScanner component
145
+ */
146
+ interface PulseScannerProps {
147
+ /** Your PulseAI API key */
148
+ apiKey: string;
149
+ /** API URL (optional - defaults to VITE_API_URL env var) */
150
+ apiUrl?: string;
151
+ /** Scan duration in seconds (default: 10) */
152
+ duration?: number;
153
+ /** Callback when vitals are successfully extracted */
154
+ onResult: (vitals: VitalsResult) => void;
155
+ /** Callback on error */
156
+ onError?: (error: Error) => void;
157
+ /** Callback for status updates */
158
+ onStatusChange?: (status: ScannerStatus) => void;
159
+ /** Show camera preview (default: true) */
160
+ showPreview?: boolean;
161
+ /** Show face detection overlay (default: true) */
162
+ showOverlay?: boolean;
163
+ /** Show face mesh lines overlay (default: false) */
164
+ showMesh?: boolean;
165
+ /** Show countdown during recording (default: true) */
166
+ showCountdown?: boolean;
167
+ /** Custom CSS class */
168
+ className?: string;
169
+ /** Theme variant */
170
+ theme?: 'light' | 'dark' | 'minimal';
171
+ /** User data for calibration */
172
+ userData?: {
173
+ age?: number;
174
+ sex?: 'male' | 'female';
175
+ weight?: number;
176
+ height?: number;
177
+ };
178
+ /** Auto-start camera on mount */
179
+ autoStart?: boolean;
180
+ }
181
+ type ScannerStatus = 'idle' | 'loading' | 'ready' | 'recording' | 'uploading' | 'analyzing' | 'complete' | 'error';
182
+ /**
183
+ * PulseScanner - Main component for capturing and analyzing vitals
184
+ *
185
+ * @example
186
+ * ```tsx
187
+ * <PulseScanner
188
+ * apiKey="pk_live_..."
189
+ * duration={10}
190
+ * onResult={(vitals) => console.log(vitals)}
191
+ * onError={(err) => console.error(err)}
192
+ * />
193
+ * ```
194
+ */
195
+ declare const PulseScanner: React$1.FC<PulseScannerProps>;
196
+
197
+ /**
198
+ * Props for VitalsCard component
199
+ */
200
+ interface VitalsCardProps {
201
+ /** Heart rate in BPM */
202
+ heartRate?: number;
203
+ /** Oxygen saturation percentage */
204
+ spo2?: number;
205
+ /** Stress index (0-100) */
206
+ stressIndex?: number;
207
+ /** HRV SDNN in milliseconds */
208
+ hrvSdnn?: number;
209
+ /** HRV RMSSD in milliseconds */
210
+ hrvRmssd?: number;
211
+ /** Respiratory rate per minute */
212
+ respiratoryRate?: number;
213
+ /** Blood pressure readings */
214
+ bloodPressure?: BloodPressure;
215
+ /** Recovery score percentage */
216
+ recoveryScore?: number;
217
+ /** Show loading state */
218
+ loading?: boolean;
219
+ /** Error message to display */
220
+ error?: string | null;
221
+ /** Compact layout */
222
+ compact?: boolean;
223
+ /** Show all metrics or just primary ones */
224
+ showAll?: boolean;
225
+ /** Custom CSS class */
226
+ className?: string;
227
+ /** Theme variant */
228
+ theme?: 'light' | 'dark';
229
+ }
230
+ /**
231
+ * VitalsCard - Display component for vital sign results
232
+ *
233
+ * @example
234
+ * ```tsx
235
+ * <VitalsCard
236
+ * heartRate={72}
237
+ * spo2={98}
238
+ * stressIndex={25}
239
+ * hrvSdnn={45}
240
+ * />
241
+ * ```
242
+ */
243
+ declare const VitalsCard: React$1.FC<VitalsCardProps>;
244
+ /**
245
+ * Helper component to create VitalsCard from full VitalsResult object
246
+ */
247
+ declare const VitalsCardFromResult: React$1.FC<{
248
+ result: VitalsResult | null;
249
+ loading?: boolean;
250
+ error?: string | null;
251
+ showAll?: boolean;
252
+ compact?: boolean;
253
+ className?: string;
254
+ theme?: 'light' | 'dark';
255
+ }>;
256
+
257
+ /**
258
+ * Configuration for PulseClient
259
+ */
260
+ interface PulseClientConfig {
261
+ /** Your PulseAI API key (starts with pk_live_ or pk_test_) */
262
+ apiKey: string;
263
+ /** API base URL (optional - defaults to VITE_API_URL env var or localhost) */
264
+ baseUrl?: string;
265
+ /** Request timeout in milliseconds (default: 60000) */
266
+ timeout?: number;
267
+ }
268
+ /**
269
+ * Options for medical vitals analysis
270
+ */
271
+ interface AnalyzeOptions {
272
+ /** Video blob to analyze (webm or mp4) */
273
+ video: Blob;
274
+ /** User's age for calibration */
275
+ age?: number;
276
+ /** User's biological sex */
277
+ sex?: 'male' | 'female';
278
+ /** User's weight in kg (for BMI calculation) */
279
+ weight?: number;
280
+ /** User's height in cm (for BMI calculation) */
281
+ height?: number;
282
+ }
283
+ /**
284
+ * Options for beauty skin analysis
285
+ */
286
+ interface BeautyAnalyzeOptions {
287
+ /** Video blob to analyze */
288
+ video: Blob;
289
+ /** User's age */
290
+ age?: number;
291
+ /** User's gender */
292
+ gender?: 'male' | 'female';
293
+ }
294
+ /**
295
+ * PulseAI API Client
296
+ *
297
+ * @example
298
+ * ```typescript
299
+ * const client = new PulseClient({ apiKey: 'pk_live_...' });
300
+ * const vitals = await client.analyze({ video: videoBlob });
301
+ * console.log(vitals.heart_rate);
302
+ * ```
303
+ */
304
+ declare class PulseClient {
305
+ private apiKey;
306
+ private baseUrl;
307
+ private timeout;
308
+ constructor(config: PulseClientConfig);
309
+ /**
310
+ * Analyze video for medical vital signs
311
+ * Returns heart rate, HRV, SpO2, stress index, and more.
312
+ */
313
+ analyze(options: AnalyzeOptions): Promise<VitalsResult>;
314
+ /**
315
+ * Analyze video for beauty/skin metrics
316
+ * Returns skin age, texture, hydration, symmetry, and recommendations.
317
+ */
318
+ analyzeBeauty(options: BeautyAnalyzeOptions): Promise<BeautyResult>;
319
+ /**
320
+ * Check API health status
321
+ */
322
+ health(): Promise<{
323
+ status: string;
324
+ service: string;
325
+ }>;
326
+ /**
327
+ * Internal request handler with timeout and error handling
328
+ */
329
+ private request;
330
+ }
331
+
332
+ interface UseCameraOptions {
333
+ width?: number;
334
+ height?: number;
335
+ facingMode?: 'user' | 'environment';
336
+ autoStart?: boolean;
337
+ }
338
+ interface UseCameraReturn {
339
+ videoRef: React.RefObject<HTMLVideoElement | null>;
340
+ stream: MediaStream | null;
341
+ isActive: boolean;
342
+ error: string | null;
343
+ start: () => Promise<void>;
344
+ stop: () => void;
345
+ }
346
+ /**
347
+ * Hook for managing camera stream.
348
+ * Handles permissions and stream lifecycle.
349
+ */
350
+ declare function useCamera(options?: UseCameraOptions): UseCameraReturn;
351
+
352
+ interface FaceDetectionResult {
353
+ detected: boolean;
354
+ landmarks: any[] | null;
355
+ qualityMessage: string;
356
+ isQualityGood: boolean;
357
+ brightness: number;
358
+ }
359
+ interface UseFaceDetectionOptions {
360
+ enabled?: boolean;
361
+ /** Canvas element for drawing mesh overlay */
362
+ meshCanvas?: HTMLCanvasElement | null;
363
+ /** Enable mesh drawing (default: false) */
364
+ drawMesh?: boolean;
365
+ }
366
+ interface UseFaceDetectionReturn extends FaceDetectionResult {
367
+ isLoading: boolean;
368
+ latestLandmarks: React.MutableRefObject<any[] | null>;
369
+ }
370
+ /**
371
+ * Hook for face detection using MediaPipe FaceLandmarker.
372
+ * Provides real-time face tracking and quality assessment.
373
+ */
374
+ declare function useFaceDetection(videoElement: HTMLVideoElement | null, options?: UseFaceDetectionOptions): UseFaceDetectionReturn;
375
+
376
+ interface UseRecordingOptions {
377
+ duration?: number;
378
+ mimeType?: string;
379
+ onComplete?: (blob: Blob) => void;
380
+ }
381
+ interface UseRecordingReturn {
382
+ isRecording: boolean;
383
+ timeLeft: number;
384
+ blob: Blob | null;
385
+ start: () => void;
386
+ stop: () => void;
387
+ reset: () => void;
388
+ }
389
+ /**
390
+ * Hook for managing video recording from a MediaStream.
391
+ * Handles countdown timer and blob generation.
392
+ */
393
+ declare function useRecording(stream: MediaStream | null, options?: UseRecordingOptions): UseRecordingReturn;
394
+
395
+ /**
396
+ * PulseAI React SDK
397
+ *
398
+ * Measure vital signs from any camera using remote photoplethysmography (rPPG).
399
+ *
400
+ * @example
401
+ * ```tsx
402
+ * import { PulseScanner, VitalsCard, PulseClient } from '@pulseai/react';
403
+ * import '@pulseai/react/styles.css';
404
+ *
405
+ * function App() {
406
+ * const [vitals, setVitals] = useState(null);
407
+ *
408
+ * return (
409
+ * <>
410
+ * <PulseScanner
411
+ * apiKey="pk_live_..."
412
+ * duration={10}
413
+ * onResult={setVitals}
414
+ * />
415
+ * {vitals && (
416
+ * <VitalsCard
417
+ * heartRate={vitals.heart_rate}
418
+ * spo2={vitals.spo2}
419
+ * stressIndex={vitals.stress_index}
420
+ * />
421
+ * )}
422
+ * </>
423
+ * );
424
+ * }
425
+ * ```
426
+ *
427
+ * @packageDocumentation
428
+ */
429
+
430
+ declare const VERSION = "1.0.0";
431
+
432
+ export { type APIError, type APIResponse, type AnalyzeOptions, type ArrhythmiaResult, type BMIEstimate, type BeautyAnalyzeOptions, type BeautyGeometry, type BeautyPhysiology, type BeautyRecommendations, type BeautyResult, type BeautySummary, type BeautySurface, type BloodPressure, type CompositeScores, type FaceDetectionResult, type MentalWellness, PulseClient, type PulseClientConfig, PulseScanner, type PulseScannerProps, type ScannerStatus, type UseCameraOptions, type UseCameraReturn, type UseFaceDetectionOptions, type UseFaceDetectionReturn, type UseRecordingOptions, type UseRecordingReturn, VERSION, type VisualAnalysis, VitalsCard, VitalsCardFromResult, type VitalsCardProps, type VitalsResult, useCamera, useFaceDetection, useRecording };