@pose-tracker/pose-estimation-web 0.1.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/LICENSE +41 -0
- package/README.md +91 -0
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/dist/index.cjs +2607 -0
- package/dist/index.d.cts +1325 -0
- package/dist/index.d.ts +1325 -0
- package/dist/index.js +2552 -0
- package/dist/pose-tracker.global.js +94 -0
- package/package.json +80 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1325 @@
|
|
|
1
|
+
import * as _tensorflow_tfjs from '@tensorflow/tfjs';
|
|
2
|
+
|
|
3
|
+
/** Bumped with package.json version (keep in sync). */
|
|
4
|
+
declare const SDK_VERSION = "0.1.0";
|
|
5
|
+
declare const SDK_NAME: "posetracker-web";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Types for the `POST /api/sdk/configure` handshake response ("manifest").
|
|
9
|
+
*
|
|
10
|
+
* The manifest is the single source of truth for server-driven behavior:
|
|
11
|
+
* exercise definitions (angle thresholds, rep state machines), reference
|
|
12
|
+
* movements, engine bundle distribution and model profiles. Updating the
|
|
13
|
+
* manifest server-side changes SDK behavior without republishing the app.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Model profiles (Sency-style). In v1 every profile maps to the bundled
|
|
17
|
+
* MoveNet SinglePose Lightning model; the fields below exist so the backend
|
|
18
|
+
* can later distribute per-profile models (e.g. Thunder for Prime, custom
|
|
19
|
+
* TFLite for native backends) without breaking older SDKs.
|
|
20
|
+
*/
|
|
21
|
+
type PoseModelProfile = 'UltraLite' | 'Lite' | 'Pro' | 'Prime' | 'AdaptiveChoice';
|
|
22
|
+
type ModelFormat = 'tfjs-graph-model' | 'tflite' | 'coreml';
|
|
23
|
+
interface ModelDescriptor {
|
|
24
|
+
/** Stable identifier, e.g. "movenet-singlepose-lightning". */
|
|
25
|
+
modelId: string;
|
|
26
|
+
format: ModelFormat;
|
|
27
|
+
/** Square input resolution in pixels (192 for MoveNet Lightning). */
|
|
28
|
+
inputSize: number;
|
|
29
|
+
/**
|
|
30
|
+
* Signed (token-gated) download URL for a standalone model archive, or
|
|
31
|
+
* null/absent when the model is delivered by another channel (stub-loader
|
|
32
|
+
* architecture: MoveNet ships inside the `pose-runtime` payload).
|
|
33
|
+
*/
|
|
34
|
+
signedUrl?: string | null;
|
|
35
|
+
/** SHA-256 hex digest of the downloadable archive (null when signedUrl is null). */
|
|
36
|
+
sha256?: string | null;
|
|
37
|
+
/** Delivery channel; 'pose-runtime' = part of the public runtime payload. */
|
|
38
|
+
deliveredBy?: 'pose-runtime';
|
|
39
|
+
version: string;
|
|
40
|
+
}
|
|
41
|
+
/** Maps each profile to a model. v1: all profiles point to the same MoveNet entry. */
|
|
42
|
+
type ModelsByProfile = Record<Exclude<PoseModelProfile, 'AdaptiveChoice'>, ModelDescriptor>;
|
|
43
|
+
/**
|
|
44
|
+
* Full movement definition, mirroring the Strapi content-types
|
|
45
|
+
* (`api::movement.movement` + steps/parameters/recommendations/initial
|
|
46
|
+
* posture) that already drive the WebView engine. The remote engine bundle
|
|
47
|
+
* consumes this structure as-is, so movements/exercises created or tuned in
|
|
48
|
+
* the dashboard update SDK behavior without any republish.
|
|
49
|
+
*/
|
|
50
|
+
interface MovementStepParameter {
|
|
51
|
+
/** Keypoint/angle selector; `a||b` accepts either side (front convention). */
|
|
52
|
+
name: string;
|
|
53
|
+
type: 'angle' | 'upper_y' | 'matching_y' | 'same_y' | 'matching_x' | 'same_x' | 'distance_y';
|
|
54
|
+
/** Weight of this parameter within the step (0–100). */
|
|
55
|
+
percentage: number;
|
|
56
|
+
operator?: string;
|
|
57
|
+
value?: number;
|
|
58
|
+
reference?: string;
|
|
59
|
+
/** When true, the parameter is included in `step_completion` payloads. */
|
|
60
|
+
public?: boolean;
|
|
61
|
+
/** Per-difficulty acceptance values (e.g. { easy: 90, medium: 100 }). */
|
|
62
|
+
scale_acceptance?: Record<string, number>;
|
|
63
|
+
/** Acceptance values when the camera point of view is elevated. */
|
|
64
|
+
upper_scale_acceptance?: Record<string, number>;
|
|
65
|
+
}
|
|
66
|
+
interface MovementRecommendation {
|
|
67
|
+
name: string;
|
|
68
|
+
type: 'angle' | 'body_axe_without_arms' | 'upper_y' | 'matching_x';
|
|
69
|
+
operator: string;
|
|
70
|
+
value?: number;
|
|
71
|
+
reference?: string;
|
|
72
|
+
recommendation_text: string;
|
|
73
|
+
scale_acceptance?: Record<string, number>;
|
|
74
|
+
upper_scale_acceptance?: Record<string, number>;
|
|
75
|
+
}
|
|
76
|
+
interface MovementStep {
|
|
77
|
+
name: string;
|
|
78
|
+
/** Order of the step within the movement (0-based). */
|
|
79
|
+
position: number;
|
|
80
|
+
/** Contribution of this step to the overall 0–100 progression. */
|
|
81
|
+
percentage: number;
|
|
82
|
+
/** Per-difficulty completion threshold to validate the step. */
|
|
83
|
+
scale_acceptance: Record<string, number>;
|
|
84
|
+
movement_parameters: MovementStepParameter[];
|
|
85
|
+
movement_recommendations: MovementRecommendation[];
|
|
86
|
+
}
|
|
87
|
+
interface InitialPosture {
|
|
88
|
+
/** Expected orientation: 'face' or 'profile'. */
|
|
89
|
+
type: string;
|
|
90
|
+
/** Keypoints that must be visible inside the placement box. */
|
|
91
|
+
required_points: string[];
|
|
92
|
+
/** Placement box paddings, in % of the frame. */
|
|
93
|
+
padding_horizontal?: number | string;
|
|
94
|
+
padding_vertical?: number | string;
|
|
95
|
+
}
|
|
96
|
+
interface MovementDefinition {
|
|
97
|
+
name: string;
|
|
98
|
+
type: 'dynamic' | 'static';
|
|
99
|
+
scale_acceptance: Record<string, number>;
|
|
100
|
+
movement_steps: MovementStep[];
|
|
101
|
+
movement_initial_posture: InitialPosture | null;
|
|
102
|
+
custom_skeleton?: string | null;
|
|
103
|
+
silhouette_image_url?: string | null;
|
|
104
|
+
}
|
|
105
|
+
interface ExerciseConfig {
|
|
106
|
+
/** Stable identifier, e.g. "squat". */
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
type: 'dynamic' | 'static';
|
|
110
|
+
/**
|
|
111
|
+
* Full server-driven definition, consumed by the remote engine bundle.
|
|
112
|
+
* Exercise configs only ever exist behind a validated handshake (live or
|
|
113
|
+
* encrypted session cache) — the npm package ships zero movement
|
|
114
|
+
* intelligence (commercial boundary, see ARCHITECTURE.md §Modes).
|
|
115
|
+
*/
|
|
116
|
+
movement: MovementDefinition;
|
|
117
|
+
}
|
|
118
|
+
interface ReferenceMovementDescriptor {
|
|
119
|
+
uuid: string;
|
|
120
|
+
name: string;
|
|
121
|
+
/** Signed URL to the movement signature JSON (angles/ratios/deltas over time). */
|
|
122
|
+
signedSignatureUrl: string;
|
|
123
|
+
sha256: string;
|
|
124
|
+
}
|
|
125
|
+
interface EngineBundleDescriptor {
|
|
126
|
+
version: string;
|
|
127
|
+
/** Signed, token-gated URL of the JS engine bundle. */
|
|
128
|
+
signedUrl: string;
|
|
129
|
+
/** SHA-256 hex digest used for integrity validation after download. */
|
|
130
|
+
sha256: string;
|
|
131
|
+
/** Minimum SDK version able to run this bundle (semver). */
|
|
132
|
+
minSdkVersion: string;
|
|
133
|
+
/** True when the version sent in `localVersions.engine` is already current. */
|
|
134
|
+
upToDate?: boolean;
|
|
135
|
+
}
|
|
136
|
+
interface PoseRuntimePartDescriptor {
|
|
137
|
+
/** Download URL of this part (`GET /api/sdk/pose-runtime?part=...&v=...`). */
|
|
138
|
+
url: string;
|
|
139
|
+
/** SHA-256 hex digest, verified BEFORE the part is committed to the cache. */
|
|
140
|
+
sha256: string;
|
|
141
|
+
bytes: number;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Descriptor of the public pose estimation payload (TF.js + MoveNet +
|
|
145
|
+
* proprietary pipeline wasm + page runtime). No API key required: pose
|
|
146
|
+
* estimation is the free tier; the movement engine stays key-gated.
|
|
147
|
+
*/
|
|
148
|
+
interface PoseRuntimeDescriptor {
|
|
149
|
+
version: string;
|
|
150
|
+
/** True when the version sent in `localVersions.poseRuntime` is current. */
|
|
151
|
+
upToDate?: boolean;
|
|
152
|
+
baseUrl?: string;
|
|
153
|
+
/** Parts by name: tfjs, tfjs-wasm, model, weights, pipeline, runtime. */
|
|
154
|
+
parts: Record<string, PoseRuntimePartDescriptor>;
|
|
155
|
+
}
|
|
156
|
+
interface ConfigureRequest {
|
|
157
|
+
/** Optional: keyless handshakes get the public manifest (pose-runtime only). */
|
|
158
|
+
apiToken?: string;
|
|
159
|
+
sdkName: 'posetracker-rn' | 'posetracker-rn-light' | 'posetracker-web';
|
|
160
|
+
sdkVersion: string;
|
|
161
|
+
targetPlatform: 'ios' | 'android' | 'web';
|
|
162
|
+
/** Requested profile; the backend may downgrade it based on plan. */
|
|
163
|
+
poseModelProfile: PoseModelProfile;
|
|
164
|
+
/** Locale for localized strings in configs (hints, recommendations). */
|
|
165
|
+
locale?: string;
|
|
166
|
+
/**
|
|
167
|
+
* Versions currently committed to the local caches; the backend answers
|
|
168
|
+
* with `upToDate` flags per module so unchanged payloads are not
|
|
169
|
+
* re-downloaded.
|
|
170
|
+
*/
|
|
171
|
+
localVersions?: {
|
|
172
|
+
poseRuntime?: string | null;
|
|
173
|
+
engine?: string | null;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
interface PlanInfo {
|
|
177
|
+
plan: string;
|
|
178
|
+
/** Remaining API calls for the current period; null = unlimited. */
|
|
179
|
+
remainingCalls: number | null;
|
|
180
|
+
commercialUse: boolean;
|
|
181
|
+
}
|
|
182
|
+
interface SdkManifest {
|
|
183
|
+
/**
|
|
184
|
+
* Short-lived session token for follow-up SDK requests (downloads, usage
|
|
185
|
+
* pings). Null on keyless handshakes (public manifest).
|
|
186
|
+
*/
|
|
187
|
+
sessionToken: string | null;
|
|
188
|
+
/** Null on keyless handshakes. */
|
|
189
|
+
plan: PlanInfo | null;
|
|
190
|
+
models: ModelsByProfile;
|
|
191
|
+
/** Profile the backend resolved for this device/plan. */
|
|
192
|
+
resolvedProfile: Exclude<PoseModelProfile, 'AdaptiveChoice'>;
|
|
193
|
+
/** Public pose estimation payload (always present, key or not). */
|
|
194
|
+
poseRuntime: PoseRuntimeDescriptor | null;
|
|
195
|
+
/** Null on keyless handshakes or when no engine bundle is deployed. */
|
|
196
|
+
engine: EngineBundleDescriptor | null;
|
|
197
|
+
exercises: ExerciseConfig[];
|
|
198
|
+
referenceMovements: ReferenceMovementDescriptor[];
|
|
199
|
+
/** Revocation signal: when true the SDK purges its sealed engine cache. */
|
|
200
|
+
revoked?: boolean;
|
|
201
|
+
/** Unix epoch ms after which the SDK should re-run the handshake. */
|
|
202
|
+
expiresAt: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Handshake client: `configure(apiToken)` → Strapi `SdkManifest`.
|
|
207
|
+
* Same endpoint as RN light; `targetPlatform: 'web'`, `sdkName: posetracker-web`.
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
declare const DEFAULT_BASE_URL = "https://movelytics-strapi-c78a339b7070.herokuapp.com";
|
|
211
|
+
interface ConfigureOptions {
|
|
212
|
+
baseUrl?: string;
|
|
213
|
+
poseModelProfile?: PoseModelProfile;
|
|
214
|
+
locale?: string;
|
|
215
|
+
localVersions?: ConfigureRequest['localVersions'];
|
|
216
|
+
}
|
|
217
|
+
declare class ConfigureError extends Error {
|
|
218
|
+
readonly code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal';
|
|
219
|
+
constructor(code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal', message: string);
|
|
220
|
+
}
|
|
221
|
+
declare function configure(apiToken: string | null, options?: ConfigureOptions): Promise<SdkManifest>;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Backend-agnostic pose types (COCO-17 MoveNet topology).
|
|
225
|
+
* Same contract as the React Native light SDK.
|
|
226
|
+
*/
|
|
227
|
+
type CocoKeypointName = 'nose' | 'left_eye' | 'right_eye' | 'left_ear' | 'right_ear' | 'left_shoulder' | 'right_shoulder' | 'left_elbow' | 'right_elbow' | 'left_wrist' | 'right_wrist' | 'left_hip' | 'right_hip' | 'left_knee' | 'right_knee' | 'left_ankle' | 'right_ankle';
|
|
228
|
+
type KeypointName = CocoKeypointName;
|
|
229
|
+
declare const COCO_KEYPOINT_NAMES: readonly CocoKeypointName[];
|
|
230
|
+
interface Keypoint {
|
|
231
|
+
name: KeypointName;
|
|
232
|
+
/** Normalized [0, 1] relative to display width (mirrored for user-facing camera). */
|
|
233
|
+
x: number;
|
|
234
|
+
/** Normalized [0, 1] relative to display height. */
|
|
235
|
+
y: number;
|
|
236
|
+
z?: number;
|
|
237
|
+
/** Confidence in [0, 1]. */
|
|
238
|
+
score: number;
|
|
239
|
+
}
|
|
240
|
+
interface Pose {
|
|
241
|
+
keypoints: Keypoint[];
|
|
242
|
+
score: number;
|
|
243
|
+
timestampMs: number;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Classic PoseTracker WebView → native message parity.
|
|
248
|
+
*
|
|
249
|
+
* Host apps that already parse `sendDataToNative` JSON
|
|
250
|
+
* (`PoseTrackerFront/lib/v3/sendDataToNativeContract.js`) can subscribe via
|
|
251
|
+
* `addMessageListener` / `usePoseTracker({ onMessage })` and keep the same
|
|
252
|
+
* `type` + payload field names (`data`, `current_count`, `message`, …).
|
|
253
|
+
*
|
|
254
|
+
* Typed {@link PoseTrackerEvent} remains the preferred API for new apps.
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
/** Loose JSON envelope matching the frozen WebView → native contract. */
|
|
258
|
+
type ClassicNativeMessage = {
|
|
259
|
+
type: string;
|
|
260
|
+
[key: string]: unknown;
|
|
261
|
+
};
|
|
262
|
+
type ClassicMessageListener = (message: ClassicNativeMessage) => void;
|
|
263
|
+
/**
|
|
264
|
+
* Map a typed SDK event to a classic PoseTracker `sendDataToNative` payload.
|
|
265
|
+
* Returns null for SDK-only events that have no classic equivalent and should
|
|
266
|
+
* not be mirrored (none today — quality/performance map to error/warning).
|
|
267
|
+
*/
|
|
268
|
+
declare function toClassicNativeMessage(event: PoseTrackerEvent): ClassicNativeMessage;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Acceleration diagnostics — web stub (browser TF.js WebGL vs WASM).
|
|
272
|
+
* Full AdaptiveChoice / Vision reports live in the RN light SDK.
|
|
273
|
+
*/
|
|
274
|
+
type AccelerationState = 'unknown' | 'gpu' | 'cpu-fallback' | 'unavailable';
|
|
275
|
+
type InferenceRuntime = 'tfjs-webgl' | 'tfjs-wasm' | 'tfjs-cpu' | 'unknown';
|
|
276
|
+
interface AccelerationDiagnostics {
|
|
277
|
+
state: AccelerationState;
|
|
278
|
+
runtime: InferenceRuntime;
|
|
279
|
+
backend: string | null;
|
|
280
|
+
glRenderer: string | null;
|
|
281
|
+
medianInferenceMs: number | null;
|
|
282
|
+
detail?: string;
|
|
283
|
+
}
|
|
284
|
+
type DiagnosticListener = (message: string) => void;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Minimal quality profile types for event parity with RN light.
|
|
288
|
+
* Full AdaptiveChoice ladder is RN/WebView-oriented; web uses getUserMedia
|
|
289
|
+
* idealWidth/idealHeight from host options instead.
|
|
290
|
+
*/
|
|
291
|
+
type QualityProfileId = 'ultra' | 'high' | 'balanced' | 'performance' | 'low' | 'minimal';
|
|
292
|
+
interface QualityProfile {
|
|
293
|
+
id: QualityProfileId;
|
|
294
|
+
idealWidth: number;
|
|
295
|
+
idealHeight: number;
|
|
296
|
+
idealFrameRate: number;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
type PoseTrackerStatus = 'idle' | 'configuring' | 'downloading' | 'warming' | 'ready' | 'error';
|
|
300
|
+
/**
|
|
301
|
+
* Operating mode (commercial boundary):
|
|
302
|
+
* - 'keypoints-only': no validated API key/session — the SDK provides its
|
|
303
|
+
* core value only (bundled MoveNet, warm-up, camera pipeline) and streams
|
|
304
|
+
* raw `keypoints` events. No business events (counter, angles, posture,
|
|
305
|
+
* progression, recommendations, form_score, exercise_summary).
|
|
306
|
+
* - 'full-engine': a handshake succeeded (live or from the encrypted local
|
|
307
|
+
* session cache) and the engine bundle is loaded — all events available.
|
|
308
|
+
*
|
|
309
|
+
* The mode can upgrade at runtime (keypoints-only → full-engine) without
|
|
310
|
+
* restarting the camera pipeline, via `configure()` or a preload retry.
|
|
311
|
+
*/
|
|
312
|
+
type PoseTrackerMode = 'keypoints-only' | 'full-engine';
|
|
313
|
+
type InitializationStep = 'configuring' | 'downloading' | 'warming' | 'accessing_webcam' | 'loading_pose_model' | 'ready';
|
|
314
|
+
interface InitializationEvent {
|
|
315
|
+
type: 'initialization';
|
|
316
|
+
/**
|
|
317
|
+
* Machine step. Classic PoseTracker used free-form `message` strings
|
|
318
|
+
* ("accessing webcam", "loading pose model", "running"); both are set.
|
|
319
|
+
*/
|
|
320
|
+
step: InitializationStep;
|
|
321
|
+
/** Human-readable status (PoseTracker WebView parity). */
|
|
322
|
+
message: string;
|
|
323
|
+
ready: boolean;
|
|
324
|
+
/** Present on the `ready` step. */
|
|
325
|
+
mode?: PoseTrackerMode;
|
|
326
|
+
/**
|
|
327
|
+
* Present on the `ready` step: GPU-acceleration verdict from the warm-up
|
|
328
|
+
* health check ('gpu' | 'cpu-fallback' | 'unavailable'). See
|
|
329
|
+
* docs/ANDROID_GL_ACCELERATION.md.
|
|
330
|
+
*/
|
|
331
|
+
acceleration?: AccelerationState;
|
|
332
|
+
}
|
|
333
|
+
interface ErrorEvent {
|
|
334
|
+
type: 'error';
|
|
335
|
+
code: 'invalid_token' | 'quota_exceeded' | 'network'
|
|
336
|
+
/**
|
|
337
|
+
* First launch, no network, empty runtime cache: the pose runtime cannot
|
|
338
|
+
* be initialized without downloading the payload once. Fatal.
|
|
339
|
+
*/
|
|
340
|
+
| 'network_required'
|
|
341
|
+
/**
|
|
342
|
+
* Offline with an API key: metered features (engine/exercises) are
|
|
343
|
+
* refused because PoseTracker cannot count their usage. Non-fatal —
|
|
344
|
+
* keypoints-only keeps running from the cache.
|
|
345
|
+
*/
|
|
346
|
+
| 'offline_metered'
|
|
347
|
+
/**
|
|
348
|
+
* The `free` plan requested developer features (angles, recommendations,
|
|
349
|
+
* progression, or keypoints combined with an exercise). Same message as
|
|
350
|
+
* the PoseTracker WebView (`TrackingAppV3`). Non-fatal: keypoints-only
|
|
351
|
+
* pose estimation keeps running.
|
|
352
|
+
*/
|
|
353
|
+
| 'free_plan_feature_blocked'
|
|
354
|
+
/**
|
|
355
|
+
* A WebView-only option was requested (blazepose, poseEngine,
|
|
356
|
+
* mediapipeModel, …): this SDK ships MoveNet Lightning only.
|
|
357
|
+
*/
|
|
358
|
+
| 'feature_not_supported'
|
|
359
|
+
/** Unknown exercise id (WebView parity: `invalid_exercise`). */
|
|
360
|
+
| 'invalid_exercise'
|
|
361
|
+
/** jump_analysis requires `userHeightCm` (WebView parity). */
|
|
362
|
+
| 'jump_analysis_missing_height' | 'model_load_failed' | 'engine_load_failed' | 'integrity_check_failed' | 'webview_error' | 'device_too_slow' | 'backend_fallback_wasm' | 'camera_denied' | 'camera_unavailable' | 'internal';
|
|
363
|
+
message: string;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Non-fatal warning the host can surface (telemetry / soft UX).
|
|
367
|
+
* Classic WebView sometimes used `type: "warning"`; V3 degradations often
|
|
368
|
+
* reuse `type: "error"` with a code — see {@link PerformanceWarningEvent}.
|
|
369
|
+
*/
|
|
370
|
+
interface WarningEvent {
|
|
371
|
+
type: 'warning';
|
|
372
|
+
code: 'quality_downgraded' | 'backend_fallback_wasm' | 'webview' | 'internal';
|
|
373
|
+
message: string;
|
|
374
|
+
timestampMs: number;
|
|
375
|
+
}
|
|
376
|
+
interface KeypointsEvent {
|
|
377
|
+
type: 'keypoints';
|
|
378
|
+
keypoints: Keypoint[];
|
|
379
|
+
/** Mean confidence of the pose. */
|
|
380
|
+
score: number;
|
|
381
|
+
timestampMs: number;
|
|
382
|
+
}
|
|
383
|
+
/** ~1 Hz telemetry pulse (web debug HUD / host metrics). */
|
|
384
|
+
interface StatsEvent {
|
|
385
|
+
type: 'stats';
|
|
386
|
+
fps: number;
|
|
387
|
+
medianInferenceMs: number | null;
|
|
388
|
+
backend: string | null;
|
|
389
|
+
keypointsAbove03: number;
|
|
390
|
+
meanScore: number;
|
|
391
|
+
videoSize: string;
|
|
392
|
+
timestampMs: number;
|
|
393
|
+
}
|
|
394
|
+
interface AngleValue {
|
|
395
|
+
/** Angle id from the exercise config, e.g. "left_knee". */
|
|
396
|
+
id: string;
|
|
397
|
+
side: 'left' | 'right' | 'center';
|
|
398
|
+
degrees: number;
|
|
399
|
+
/** Min confidence of the three keypoints defining the angle. */
|
|
400
|
+
score: number;
|
|
401
|
+
}
|
|
402
|
+
interface AnglesEvent {
|
|
403
|
+
type: 'angles';
|
|
404
|
+
angles: AngleValue[];
|
|
405
|
+
timestampMs: number;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Letter grade for a 0–100 form score (GitBook / Front BaseExercise):
|
|
409
|
+
* A ≥ 90, B ≥ 80, C ≥ 70, D ≥ 60, F < 60.
|
|
410
|
+
*/
|
|
411
|
+
type FormGrade = 'A' | 'B' | 'C' | 'D' | 'F';
|
|
412
|
+
/** Valid `minGrade` filter values (F is not a valid minGrade config). */
|
|
413
|
+
type MinGrade = 'A' | 'B' | 'C' | 'D';
|
|
414
|
+
/**
|
|
415
|
+
* Nested form score on a counted rep — GitBook / WebView V3
|
|
416
|
+
* `counter.form_score` (authoritative grade for that rep).
|
|
417
|
+
*/
|
|
418
|
+
interface CounterFormScore {
|
|
419
|
+
/** Score of the rep that was just counted, 0–100. */
|
|
420
|
+
score: number;
|
|
421
|
+
/** Running average across counted reps, 0–100. */
|
|
422
|
+
average: number;
|
|
423
|
+
grade: FormGrade;
|
|
424
|
+
}
|
|
425
|
+
interface CounterEvent {
|
|
426
|
+
type: 'counter';
|
|
427
|
+
count: number;
|
|
428
|
+
/**
|
|
429
|
+
* Authoritative form score for the counted rep (WebView
|
|
430
|
+
* `counter.form_score`). Prefer this over a standalone `form_score` event
|
|
431
|
+
* when mirroring the tracking endpoint.
|
|
432
|
+
*/
|
|
433
|
+
formScore?: CounterFormScore;
|
|
434
|
+
/** Similarity score vs. reference movement for the last rep, when a reference is active. */
|
|
435
|
+
referenceScore?: number;
|
|
436
|
+
timestampMs: number;
|
|
437
|
+
}
|
|
438
|
+
interface PostureEvent {
|
|
439
|
+
type: 'posture';
|
|
440
|
+
/** True when the user placement satisfies the exercise posture requirements. */
|
|
441
|
+
ready: boolean;
|
|
442
|
+
/** Localized human hint ("Face the camera", "Get ready…"). */
|
|
443
|
+
hint: string;
|
|
444
|
+
/** WebView parity: placement direction hint ("in-frame", "face-camera", "profile-camera"). */
|
|
445
|
+
direction?: string;
|
|
446
|
+
missingKeypoints: string[];
|
|
447
|
+
timestampMs: number;
|
|
448
|
+
}
|
|
449
|
+
interface ProgressionEvent {
|
|
450
|
+
type: 'progression';
|
|
451
|
+
/** Movement progression within the current rep, 0–100. */
|
|
452
|
+
value: number;
|
|
453
|
+
timestampMs: number;
|
|
454
|
+
}
|
|
455
|
+
interface RecommendationsEvent {
|
|
456
|
+
type: 'recommendations';
|
|
457
|
+
/** Localized form advice strings. */
|
|
458
|
+
recommendations: string[];
|
|
459
|
+
timestampMs: number;
|
|
460
|
+
}
|
|
461
|
+
interface FormScoreEvent {
|
|
462
|
+
type: 'form_score';
|
|
463
|
+
/** Score of the last completed rep, 0–100. */
|
|
464
|
+
score: number;
|
|
465
|
+
/** Running average across the session, 0–100. */
|
|
466
|
+
average: number;
|
|
467
|
+
grade: FormGrade;
|
|
468
|
+
timestampMs: number;
|
|
469
|
+
}
|
|
470
|
+
interface RepSummary {
|
|
471
|
+
index: number;
|
|
472
|
+
formScore: number;
|
|
473
|
+
durationMs: number;
|
|
474
|
+
referenceScore?: number;
|
|
475
|
+
}
|
|
476
|
+
interface ExerciseSummaryEvent {
|
|
477
|
+
type: 'exercise_summary';
|
|
478
|
+
exercise: string;
|
|
479
|
+
counter: number;
|
|
480
|
+
averageFormScore: number;
|
|
481
|
+
averageSimilarity?: number;
|
|
482
|
+
grade: FormGrade;
|
|
483
|
+
history: RepSummary[];
|
|
484
|
+
durationMs: number;
|
|
485
|
+
timestampMs: number;
|
|
486
|
+
}
|
|
487
|
+
interface VisibleHipsInfo {
|
|
488
|
+
left: boolean;
|
|
489
|
+
right: boolean;
|
|
490
|
+
}
|
|
491
|
+
/** jump_analysis only: cm/pixel calibration established from userHeightCm. */
|
|
492
|
+
interface JumpCalibrationEvent {
|
|
493
|
+
type: 'jump_calibration';
|
|
494
|
+
calibrated: true;
|
|
495
|
+
cmPerPixel: number;
|
|
496
|
+
baselineY: number | null;
|
|
497
|
+
visibleHips: VisibleHipsInfo;
|
|
498
|
+
/** WebView literal: "Calibration complete - ready to jump". */
|
|
499
|
+
message: string;
|
|
500
|
+
timestampMs: number;
|
|
501
|
+
}
|
|
502
|
+
/** Push-off detected — measurement in progress. */
|
|
503
|
+
interface JumpStartedEvent {
|
|
504
|
+
type: 'jump_started';
|
|
505
|
+
/** WebView literal: "Jump detected - measuring height". */
|
|
506
|
+
message: string;
|
|
507
|
+
timestampMs: number;
|
|
508
|
+
}
|
|
509
|
+
/** Live/final jump height. `final: true` closes the jump (see JumpResultEvent). */
|
|
510
|
+
interface JumpHeightEvent {
|
|
511
|
+
type: 'jump_height';
|
|
512
|
+
jumpHeightCm: number;
|
|
513
|
+
baselineY?: number | null;
|
|
514
|
+
minY?: number | null;
|
|
515
|
+
measuring: boolean;
|
|
516
|
+
landed: boolean;
|
|
517
|
+
final?: boolean;
|
|
518
|
+
deltaPixels?: number;
|
|
519
|
+
sideUsed?: string | null;
|
|
520
|
+
airTimeMs?: number;
|
|
521
|
+
airTimeSeconds?: number;
|
|
522
|
+
visibleHips?: VisibleHipsInfo;
|
|
523
|
+
timestampMs: number;
|
|
524
|
+
}
|
|
525
|
+
/** Detection aborted (approach toward camera, timeout, tracking lost…). */
|
|
526
|
+
interface JumpDiscardedEvent {
|
|
527
|
+
type: 'jump_discarded';
|
|
528
|
+
reason: string;
|
|
529
|
+
userMessage: string;
|
|
530
|
+
timestampMs: number;
|
|
531
|
+
}
|
|
532
|
+
/** One completed jump (final height for jump N). */
|
|
533
|
+
interface JumpResultEvent {
|
|
534
|
+
type: 'jump_result';
|
|
535
|
+
jumpNumber: number;
|
|
536
|
+
jumpHeightCm: number;
|
|
537
|
+
airTimeMs?: number;
|
|
538
|
+
airTimeSeconds?: number;
|
|
539
|
+
baselineY?: number | null;
|
|
540
|
+
minY?: number | null;
|
|
541
|
+
deltaPixels?: number;
|
|
542
|
+
sideUsed?: string | null;
|
|
543
|
+
visibleHips?: VisibleHipsInfo;
|
|
544
|
+
timestampMs: number;
|
|
545
|
+
}
|
|
546
|
+
interface JumpSummaryEntry {
|
|
547
|
+
jumpNumber: number;
|
|
548
|
+
jumpHeightCm: number;
|
|
549
|
+
airTimeMs?: number;
|
|
550
|
+
airTimeSeconds?: number;
|
|
551
|
+
}
|
|
552
|
+
/** Running summary of all completed jumps (emitted after each jump_result). */
|
|
553
|
+
interface JumpSummaryEvent {
|
|
554
|
+
type: 'jump_summary';
|
|
555
|
+
totalJumps: number;
|
|
556
|
+
avgJumpHeight: number;
|
|
557
|
+
maxJumpHeight: number;
|
|
558
|
+
minJumpHeight: number;
|
|
559
|
+
avgAirTimeSeconds?: number;
|
|
560
|
+
jumps: JumpSummaryEntry[];
|
|
561
|
+
timestampMs: number;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Camera / preprocess quality tier changed (AdaptiveChoice auto-downgrade,
|
|
565
|
+
* crash-guard recovery, or GL capability hint). English, for app developers.
|
|
566
|
+
*/
|
|
567
|
+
interface QualityChangedEvent {
|
|
568
|
+
type: 'quality_changed';
|
|
569
|
+
previousProfile: QualityProfileId;
|
|
570
|
+
activeProfile: QualityProfileId;
|
|
571
|
+
reason: 'low_fps' | 'device_capability' | 'crash_guard' | 'warmup_estimate' | 'manual';
|
|
572
|
+
detail: string;
|
|
573
|
+
profile: QualityProfile;
|
|
574
|
+
/** Present when reason is warmup_estimate / low_fps. */
|
|
575
|
+
estimatedFps?: number | null;
|
|
576
|
+
medianInferenceMs?: number | null;
|
|
577
|
+
timestampMs: number;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Fired when mean inference FPS stays critically low. English developer alert —
|
|
581
|
+
* surface in your own telemetry / support tooling; do not show raw to end users
|
|
582
|
+
* unless you localize it.
|
|
583
|
+
*/
|
|
584
|
+
interface PerformanceWarningEvent {
|
|
585
|
+
type: 'performance_warning';
|
|
586
|
+
code: 'device_too_slow';
|
|
587
|
+
message: string;
|
|
588
|
+
meanFps: number;
|
|
589
|
+
thresholdFps: number;
|
|
590
|
+
activeProfile: QualityProfileId;
|
|
591
|
+
medianInferenceMs: number | null;
|
|
592
|
+
videoSize?: string;
|
|
593
|
+
timestampMs: number;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Download progress of the pose-runtime payload (first preload, or version
|
|
597
|
+
* update). Lets the host app show a loader/progress bar during the initial
|
|
598
|
+
* ~9 MB download; never fired when the cache is already up-to-date.
|
|
599
|
+
*/
|
|
600
|
+
interface RuntimeDownloadProgressEvent {
|
|
601
|
+
type: 'runtime_download_progress';
|
|
602
|
+
/** Part being downloaded (tfjs, tfjs-wasm, model, weights, pipeline, runtime). */
|
|
603
|
+
part: string;
|
|
604
|
+
completedParts: number;
|
|
605
|
+
totalParts: number;
|
|
606
|
+
/** Size of the current part, from the signed manifest. */
|
|
607
|
+
partBytes: number;
|
|
608
|
+
timestampMs: number;
|
|
609
|
+
}
|
|
610
|
+
type PoseTrackerEvent = InitializationEvent | ErrorEvent | WarningEvent | RuntimeDownloadProgressEvent | KeypointsEvent | StatsEvent | AnglesEvent | CounterEvent | PostureEvent | ProgressionEvent | RecommendationsEvent | FormScoreEvent | ExerciseSummaryEvent | JumpCalibrationEvent | JumpStartedEvent | JumpHeightEvent | JumpDiscardedEvent | JumpResultEvent | JumpSummaryEvent | QualityChangedEvent | PerformanceWarningEvent;
|
|
611
|
+
type PoseTrackerEventType = PoseTrackerEvent['type'];
|
|
612
|
+
type PoseTrackerEventListener = (event: PoseTrackerEvent) => void;
|
|
613
|
+
/**
|
|
614
|
+
* Typed per-event callbacks, convenient for `usePoseTracker({ onCounter, ... })`.
|
|
615
|
+
* Also accepts {@link onMessage} for classic PoseTracker WebView JSON parity.
|
|
616
|
+
*/
|
|
617
|
+
interface PoseTrackerCallbacks {
|
|
618
|
+
onInitialization?: (event: InitializationEvent) => void;
|
|
619
|
+
onError?: (event: ErrorEvent) => void;
|
|
620
|
+
onWarning?: (event: WarningEvent) => void;
|
|
621
|
+
onKeypoints?: (event: KeypointsEvent) => void;
|
|
622
|
+
onStats?: (event: StatsEvent) => void;
|
|
623
|
+
onAngles?: (event: AnglesEvent) => void;
|
|
624
|
+
onCounter?: (event: CounterEvent) => void;
|
|
625
|
+
onPosture?: (event: PostureEvent) => void;
|
|
626
|
+
onProgression?: (event: ProgressionEvent) => void;
|
|
627
|
+
onRecommendations?: (event: RecommendationsEvent) => void;
|
|
628
|
+
onFormScore?: (event: FormScoreEvent) => void;
|
|
629
|
+
onExerciseSummary?: (event: ExerciseSummaryEvent) => void;
|
|
630
|
+
onJumpCalibration?: (event: JumpCalibrationEvent) => void;
|
|
631
|
+
onJumpStarted?: (event: JumpStartedEvent) => void;
|
|
632
|
+
onJumpHeight?: (event: JumpHeightEvent) => void;
|
|
633
|
+
onJumpDiscarded?: (event: JumpDiscardedEvent) => void;
|
|
634
|
+
onJumpResult?: (event: JumpResultEvent) => void;
|
|
635
|
+
onJumpSummary?: (event: JumpSummaryEvent) => void;
|
|
636
|
+
onQualityChanged?: (event: QualityChangedEvent) => void;
|
|
637
|
+
onPerformanceWarning?: (event: PerformanceWarningEvent) => void;
|
|
638
|
+
onRuntimeDownloadProgress?: (event: RuntimeDownloadProgressEvent) => void;
|
|
639
|
+
/**
|
|
640
|
+
* Classic PoseTracker `sendDataToNative` JSON stream (same `type` / field
|
|
641
|
+
* names as the WebView product). Fires for every typed event.
|
|
642
|
+
*/
|
|
643
|
+
onMessage?: (message: ClassicNativeMessage) => void;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Contract between the SDK shell and the engine (business logic: angles,
|
|
648
|
+
* rep counting, form scoring, posture, progression, recommendations).
|
|
649
|
+
*
|
|
650
|
+
* The engine is EXCLUSIVELY distributed remotely as a versioned JS bundle
|
|
651
|
+
* (signed URL in the handshake manifest, SHA-256 integrity check, sealed
|
|
652
|
+
* local cache) — the npm package ships zero business logic. Without a
|
|
653
|
+
* loaded engine the SDK runs in keypoints-only mode.
|
|
654
|
+
*/
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Feature flags forwarded to the engine session (WebView query-param
|
|
658
|
+
* parity). The client already filters emissions by plan/flags; passing
|
|
659
|
+
* them to the engine is a second gate AND lets the engine skip work.
|
|
660
|
+
*/
|
|
661
|
+
interface EngineSessionFeatures {
|
|
662
|
+
angles?: boolean;
|
|
663
|
+
recommendations?: boolean;
|
|
664
|
+
progression?: boolean;
|
|
665
|
+
}
|
|
666
|
+
interface EngineSessionOptions {
|
|
667
|
+
exercise: ExerciseConfig;
|
|
668
|
+
/** Locale used to resolve localized hints/recommendations. */
|
|
669
|
+
locale: string;
|
|
670
|
+
/** Difficulty key into the movement `scale_acceptance` maps. Default: 'medium'. */
|
|
671
|
+
difficulty?: string;
|
|
672
|
+
/** Parsed reference-movement signature, when comparing against a reference. */
|
|
673
|
+
referenceSignature?: unknown;
|
|
674
|
+
/**
|
|
675
|
+
* Only count reps whose form grade is at or above this letter (`A` best).
|
|
676
|
+
* WebView `minGrade` parity: reps below the bar emit neither `counter`
|
|
677
|
+
* nor `form_score` and are excluded from the summary.
|
|
678
|
+
*/
|
|
679
|
+
minGrade?: MinGrade;
|
|
680
|
+
/** Emission flags — engines older than 1.1.0 ignore them (client re-filters). */
|
|
681
|
+
features?: EngineSessionFeatures;
|
|
682
|
+
}
|
|
683
|
+
interface EngineSession {
|
|
684
|
+
/** Feed one estimated pose; the session emits events through the sink. */
|
|
685
|
+
processPose(pose: Pose): void;
|
|
686
|
+
/** Finish the session; emits a final `exercise_summary` event. */
|
|
687
|
+
end(): void;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Custom (non-FSM) exercise shipped inside the engine bundle, e.g. the jump
|
|
691
|
+
* analysis handlers (WebView `customHandlers.js` parity). Not present in the
|
|
692
|
+
* Strapi movement manifest — discovered via `listCustomExercises()`.
|
|
693
|
+
*/
|
|
694
|
+
interface CustomExerciseDescriptor {
|
|
695
|
+
/** Exercise id used with `startExercise()` (e.g. 'jump_analysis'). */
|
|
696
|
+
id: string;
|
|
697
|
+
displayName: string;
|
|
698
|
+
type: 'custom';
|
|
699
|
+
description?: string;
|
|
700
|
+
/** Option keys that MUST be passed to `startExercise` (e.g. 'userHeightCm'). */
|
|
701
|
+
requiredParams: string[];
|
|
702
|
+
optionalParams: string[];
|
|
703
|
+
}
|
|
704
|
+
/** Options for engine custom sessions (jump_analysis / air_time_jump). */
|
|
705
|
+
interface CustomSessionOptions {
|
|
706
|
+
exerciseId: string;
|
|
707
|
+
locale: string;
|
|
708
|
+
/** Athlete height in cm — required by jump_analysis (cm/pixel calibration). */
|
|
709
|
+
userHeightCm?: number;
|
|
710
|
+
/** Device pitch in degrees, used to compensate camera tilt. */
|
|
711
|
+
devicePitchDeg?: number;
|
|
712
|
+
}
|
|
713
|
+
type EventSink = (event: PoseTrackerEvent) => void;
|
|
714
|
+
interface PoseTrackerEngine {
|
|
715
|
+
readonly version: string;
|
|
716
|
+
createSession(options: EngineSessionOptions, emit: EventSink): EngineSession;
|
|
717
|
+
/** Engine >= 1.2.0: custom exercises shipped in the bundle (jump analysis…). */
|
|
718
|
+
listCustomExercises?(): CustomExerciseDescriptor[];
|
|
719
|
+
/** Engine >= 1.2.0: start a custom exercise session (placement + jump_* events). */
|
|
720
|
+
createCustomSession?(options: CustomSessionOptions, emit: EventSink): EngineSession;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Remote engine distribution for the browser (same contract as RN light):
|
|
725
|
+
* signed URL → SHA-256 → evaluate via `new Function` → sealed localStorage cache.
|
|
726
|
+
*/
|
|
727
|
+
|
|
728
|
+
interface FileStore {
|
|
729
|
+
read(key: string): Promise<string | null>;
|
|
730
|
+
write(key: string, contents: string): Promise<void>;
|
|
731
|
+
remove(key: string): Promise<void>;
|
|
732
|
+
}
|
|
733
|
+
interface KeyValueStore {
|
|
734
|
+
getItem(key: string): Promise<string | null>;
|
|
735
|
+
setItem(key: string, value: string): Promise<void>;
|
|
736
|
+
removeItem(key: string): Promise<void>;
|
|
737
|
+
}
|
|
738
|
+
/** localStorage-backed KV (guard + track queue). */
|
|
739
|
+
declare function createLocalKeyValueStore(): KeyValueStore | null;
|
|
740
|
+
/**
|
|
741
|
+
* localStorage file store for sealed engine bundles.
|
|
742
|
+
* Large engines may exceed ~5 MB quota — download still works; cache is best-effort.
|
|
743
|
+
*/
|
|
744
|
+
declare function createLocalFileStore(): FileStore | null;
|
|
745
|
+
declare function createMemoryFileStore(): FileStore;
|
|
746
|
+
/** Best available browser store: localStorage → memory. */
|
|
747
|
+
declare function createWebFileStore(): FileStore;
|
|
748
|
+
interface EngineLoadResult {
|
|
749
|
+
engine: PoseTrackerEngine;
|
|
750
|
+
source: 'remote-cache' | 'remote-download';
|
|
751
|
+
}
|
|
752
|
+
interface EngineLoaderOptions {
|
|
753
|
+
fileStore?: FileStore | null;
|
|
754
|
+
keyValueStore?: KeyValueStore | null;
|
|
755
|
+
fetchFn?: typeof fetch;
|
|
756
|
+
onDiagnostic?: (message: string) => void;
|
|
757
|
+
}
|
|
758
|
+
declare class EngineLoader {
|
|
759
|
+
private readonly files;
|
|
760
|
+
private readonly kv;
|
|
761
|
+
private readonly fetchFn;
|
|
762
|
+
private readonly onDiagnostic;
|
|
763
|
+
lastError: string | null;
|
|
764
|
+
constructor(options?: EngineLoaderOptions);
|
|
765
|
+
clearGuard(descriptor: EngineBundleDescriptor | null): Promise<void>;
|
|
766
|
+
load(descriptor: EngineBundleDescriptor | null, cacheSecret: string, options?: {
|
|
767
|
+
forceRetry?: boolean;
|
|
768
|
+
}): Promise<EngineLoadResult | null>;
|
|
769
|
+
private evaluate;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Usage tracking — `POST /api/sdk/track` (same contract as RN light).
|
|
774
|
+
* Browser: platform = 'web'; anonymous queue → localStorage.
|
|
775
|
+
*/
|
|
776
|
+
|
|
777
|
+
declare class TrackError extends Error {
|
|
778
|
+
readonly code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal';
|
|
779
|
+
constructor(code: 'invalid_token' | 'quota_exceeded' | 'network' | 'internal', message: string);
|
|
780
|
+
}
|
|
781
|
+
interface TrackRequest {
|
|
782
|
+
event?: string;
|
|
783
|
+
apiToken?: string | null;
|
|
784
|
+
params?: Record<string, unknown>;
|
|
785
|
+
}
|
|
786
|
+
interface TrackResponse {
|
|
787
|
+
tracked: boolean;
|
|
788
|
+
anonymous?: boolean;
|
|
789
|
+
monthly_usage_counter?: number;
|
|
790
|
+
remainingCalls?: number;
|
|
791
|
+
type?: string;
|
|
792
|
+
}
|
|
793
|
+
interface UsageTrackerOptions {
|
|
794
|
+
baseUrl?: string;
|
|
795
|
+
keyValueStore?: KeyValueStore | null;
|
|
796
|
+
fetchFn?: typeof fetch;
|
|
797
|
+
}
|
|
798
|
+
declare class UsageTracker {
|
|
799
|
+
private readonly baseUrl;
|
|
800
|
+
private readonly kv;
|
|
801
|
+
private readonly fetchFn;
|
|
802
|
+
constructor(options?: UsageTrackerOptions);
|
|
803
|
+
trackMetered(request: TrackRequest & {
|
|
804
|
+
apiToken: string;
|
|
805
|
+
}): Promise<TrackResponse>;
|
|
806
|
+
trackAnonymous(request?: TrackRequest): Promise<void>;
|
|
807
|
+
flushQueue(): Promise<void>;
|
|
808
|
+
private post;
|
|
809
|
+
private readQueue;
|
|
810
|
+
private writeQueue;
|
|
811
|
+
private enqueue;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Online pose-model catalog (web) — mirrors RN light `poseModels.ts`.
|
|
816
|
+
*
|
|
817
|
+
* Default: MoveNet SinglePose Lightning at app.posetracker.com.
|
|
818
|
+
* BlazePose: handled by the pose-detection adapter (no graph modelUrl).
|
|
819
|
+
*/
|
|
820
|
+
/** Production MoveNet SinglePose Lightning topology (Front / RN light default). */
|
|
821
|
+
declare const DEFAULT_MOVENET_LIGHTNING_URL = "https://app.posetracker.com/scripts/tmp_model_to_remove.json";
|
|
822
|
+
type PoseModelAlias = 'movenet' | 'movenet-singlepose-lightning' | 'lightning' | 'blazepose' | (string & {});
|
|
823
|
+
interface ResolvePoseModelOptions {
|
|
824
|
+
model?: PoseModelAlias;
|
|
825
|
+
/** Explicit TF.js graph-model topology URL (weights resolve as siblings). */
|
|
826
|
+
modelUrl?: string;
|
|
827
|
+
}
|
|
828
|
+
type PoseModelKind = 'movenet-graph' | 'blazepose' | 'custom-graph' | 'unsupported';
|
|
829
|
+
interface ResolvedPoseModel {
|
|
830
|
+
modelId: string;
|
|
831
|
+
kind: PoseModelKind;
|
|
832
|
+
/** TF.js `loadGraphModel` URL when kind is movenet-graph or custom-graph. */
|
|
833
|
+
modelUrl: string | null;
|
|
834
|
+
unsupportedReason?: string;
|
|
835
|
+
}
|
|
836
|
+
declare function resolvePoseModel(options?: ResolvePoseModelOptions): ResolvedPoseModel;
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Host-selectable tracking features — same plan gating / messages as RN light
|
|
840
|
+
* and PoseTracker WebView `TrackingAppV3`.
|
|
841
|
+
*
|
|
842
|
+
* Web delta: `model: 'blazepose'` is a first-class model option (separate from
|
|
843
|
+
* the unsupported WebView `features.blazepose` query-param key).
|
|
844
|
+
*/
|
|
845
|
+
|
|
846
|
+
interface PoseTrackerFeatures {
|
|
847
|
+
angles?: boolean;
|
|
848
|
+
recommendations?: boolean;
|
|
849
|
+
progression?: boolean;
|
|
850
|
+
keypoints?: boolean;
|
|
851
|
+
minGrade?: MinGrade;
|
|
852
|
+
}
|
|
853
|
+
interface ResolvedFeatures {
|
|
854
|
+
angles: boolean;
|
|
855
|
+
recommendations: boolean;
|
|
856
|
+
progression: boolean;
|
|
857
|
+
keypoints: boolean;
|
|
858
|
+
minGrade: MinGrade | null;
|
|
859
|
+
}
|
|
860
|
+
declare const DEFAULT_FEATURES: ResolvedFeatures;
|
|
861
|
+
declare const FREE_PLAN_FEATURES_MESSAGE = "You cannot use developer features. (visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)";
|
|
862
|
+
declare const INVALID_TOKEN_MESSAGE = "Invalid params. Please refer to the documentation and set token=YOUR API_KEY et exercise=A correct exercise. (visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)";
|
|
863
|
+
declare const COMBINED_REFERENCE_EXERCISE_MESSAGE = "You cannot combine reference & exercise. Please check the documentation.";
|
|
864
|
+
declare function featureNotSupportedMessage(key: string): string;
|
|
865
|
+
declare function resolveFeatures(input: PoseTrackerFeatures | undefined): {
|
|
866
|
+
features: ResolvedFeatures;
|
|
867
|
+
unsupportedKeys: string[];
|
|
868
|
+
};
|
|
869
|
+
declare function freeBlockedFeatures(features: ResolvedFeatures, options: {
|
|
870
|
+
withExercise: boolean;
|
|
871
|
+
}): string[];
|
|
872
|
+
declare function isPaidPlan(planType: string | null | undefined): boolean;
|
|
873
|
+
declare function shouldShowWatermark(planType: string | null | undefined): boolean;
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Cold-start / preload modes for {@link PoseTrackerClient.preload}.
|
|
877
|
+
*
|
|
878
|
+
* - `basic` (default): load MoveNet + warm TF.js/WebGL with zero tensors.
|
|
879
|
+
* Does **not** call `getUserMedia` — no unexpected camera permission prompt.
|
|
880
|
+
* - `full`: also open the camera during warm-up (legacy behaviour). Only use
|
|
881
|
+
* when the host UI is already in a camera context the user expects.
|
|
882
|
+
*/
|
|
883
|
+
type ColdStartMode = 'basic' | 'full';
|
|
884
|
+
interface PreloadOptions {
|
|
885
|
+
/**
|
|
886
|
+
* Cold-start depth. Default `basic`.
|
|
887
|
+
* Pass `full` only when you intentionally want getUserMedia during preload
|
|
888
|
+
* (e.g. a visible camera screen that also warms the model).
|
|
889
|
+
*/
|
|
890
|
+
coldStart?: ColdStartMode;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Skeleton overlay definition — same JSON shape as Strapi `api::skeleton.skeleton`
|
|
895
|
+
* / PoseTrackerFront `DEFAULT_SKELETON` (WebView `?skeleton=<api_uuid>`).
|
|
896
|
+
*/
|
|
897
|
+
interface SkeletonCirclesStyle {
|
|
898
|
+
circleFillColor: string;
|
|
899
|
+
circleStrokeColor: string;
|
|
900
|
+
strokeWidth: string | number;
|
|
901
|
+
radius: string | number;
|
|
902
|
+
}
|
|
903
|
+
interface SkeletonLinesStyle {
|
|
904
|
+
lineStrokeColor: string;
|
|
905
|
+
strokeWidth: string | number;
|
|
906
|
+
}
|
|
907
|
+
interface SkeletonAnglesStyle {
|
|
908
|
+
fontColor?: string;
|
|
909
|
+
strokeColor?: string;
|
|
910
|
+
}
|
|
911
|
+
interface SkeletonDefinition {
|
|
912
|
+
keypoints: string[];
|
|
913
|
+
/** Named pairs `"left_hip||left_knee"`. */
|
|
914
|
+
keypoint_lines: string[];
|
|
915
|
+
keypoint_angles?: string[];
|
|
916
|
+
circles: SkeletonCirclesStyle;
|
|
917
|
+
lines: SkeletonLinesStyle;
|
|
918
|
+
angles?: SkeletonAnglesStyle;
|
|
919
|
+
}
|
|
920
|
+
/** PoseTracker API default (navy `#010A73` + gold `#FFC300`). */
|
|
921
|
+
declare const DEFAULT_SKELETON_DEFINITION: SkeletonDefinition;
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Browser PoseTracker client — DOM camera + TF.js keypoints + remote engine.
|
|
925
|
+
* Public surface mirrors RN light `PoseTrackerClient` where the browser allows.
|
|
926
|
+
*/
|
|
927
|
+
|
|
928
|
+
interface StartExerciseOptions {
|
|
929
|
+
difficulty?: string;
|
|
930
|
+
userHeightCm?: number;
|
|
931
|
+
devicePitchDeg?: number;
|
|
932
|
+
}
|
|
933
|
+
interface PoseTrackerClientOptions extends ConfigureOptions {
|
|
934
|
+
model?: PoseModelAlias;
|
|
935
|
+
modelUrl?: string;
|
|
936
|
+
apiToken?: string;
|
|
937
|
+
facingMode?: 'user' | 'environment';
|
|
938
|
+
/** RN `position` alias: front → user, back → environment. */
|
|
939
|
+
position?: 'front' | 'back';
|
|
940
|
+
drawSkeleton?: boolean;
|
|
941
|
+
drawPlacementBox?: boolean;
|
|
942
|
+
placementPaddingPercent?: number;
|
|
943
|
+
loadingText?: string;
|
|
944
|
+
showWatermark?: boolean;
|
|
945
|
+
debugHud?: boolean;
|
|
946
|
+
features?: PoseTrackerFeatures;
|
|
947
|
+
idealWidth?: number;
|
|
948
|
+
idealHeight?: number;
|
|
949
|
+
skeletonDef?: SkeletonDefinition | null;
|
|
950
|
+
skeletonUuid?: string | null;
|
|
951
|
+
engineLoader?: EngineLoader;
|
|
952
|
+
fileStore?: FileStore | null;
|
|
953
|
+
usageTracker?: UsageTracker;
|
|
954
|
+
onDiagnostic?: (message: string) => void;
|
|
955
|
+
/** Alias of preload coldStart when using start(). Default full on camera screens. */
|
|
956
|
+
coldStart?: ColdStartMode;
|
|
957
|
+
}
|
|
958
|
+
/** @deprecated Prefer PoseTrackerClientOptions — kept for vanilla DX. */
|
|
959
|
+
type PoseTrackerOptions = PoseTrackerClientOptions;
|
|
960
|
+
type PoseTracker = PoseTrackerClient;
|
|
961
|
+
declare class PoseTrackerClient {
|
|
962
|
+
private status;
|
|
963
|
+
private mode;
|
|
964
|
+
private lastError;
|
|
965
|
+
private manifest;
|
|
966
|
+
private engine;
|
|
967
|
+
private engineSource;
|
|
968
|
+
private session;
|
|
969
|
+
private currentExerciseId;
|
|
970
|
+
private apiToken;
|
|
971
|
+
private readonly engineLoader;
|
|
972
|
+
private readonly files;
|
|
973
|
+
private readonly tracker;
|
|
974
|
+
private readonly listeners;
|
|
975
|
+
private readonly typedListeners;
|
|
976
|
+
private readonly messageListeners;
|
|
977
|
+
private readonly stateListeners;
|
|
978
|
+
private preloadPromise;
|
|
979
|
+
private coldStartMode;
|
|
980
|
+
private configurePromise;
|
|
981
|
+
private handshakePromise;
|
|
982
|
+
private meteredSessionState;
|
|
983
|
+
private lastCameraStartInfo;
|
|
984
|
+
private sessionErrorStreak;
|
|
985
|
+
private readonly features;
|
|
986
|
+
private readonly unsupportedFeatureKeys;
|
|
987
|
+
private featureGateReported;
|
|
988
|
+
private keypointsSuppressionLogged;
|
|
989
|
+
private opts;
|
|
990
|
+
private shell;
|
|
991
|
+
private stream;
|
|
992
|
+
private adapter;
|
|
993
|
+
private resolved;
|
|
994
|
+
private running;
|
|
995
|
+
private busy;
|
|
996
|
+
private loopToken;
|
|
997
|
+
private cameraRevealed;
|
|
998
|
+
private disposed;
|
|
999
|
+
private skeletonDef;
|
|
1000
|
+
private acceleration;
|
|
1001
|
+
private inferMs;
|
|
1002
|
+
private fpsWindow;
|
|
1003
|
+
private fpsTimer;
|
|
1004
|
+
constructor(apiToken?: string, options?: PoseTrackerClientOptions);
|
|
1005
|
+
getStatus(): PoseTrackerStatus;
|
|
1006
|
+
getMode(): PoseTrackerMode;
|
|
1007
|
+
getError(): ErrorEvent | null;
|
|
1008
|
+
getManifest(): SdkManifest | null;
|
|
1009
|
+
getPlanType(): string | null;
|
|
1010
|
+
getFeatures(): ResolvedFeatures;
|
|
1011
|
+
getEngineSource(): EngineLoadResult['source'] | null;
|
|
1012
|
+
getAcceleration(): AccelerationState;
|
|
1013
|
+
getAccelerationDiagnostics(): AccelerationDiagnostics;
|
|
1014
|
+
getResolvedModel(): ResolvedPoseModel;
|
|
1015
|
+
getColdStartMode(): ColdStartMode;
|
|
1016
|
+
addEventListener(listener: PoseTrackerEventListener): () => void;
|
|
1017
|
+
on(type: PoseTrackerEvent['type'] | '*', listener: PoseTrackerEventListener): () => void;
|
|
1018
|
+
addMessageListener(listener: ClassicMessageListener): () => void;
|
|
1019
|
+
onStateChange(listener: () => void): () => void;
|
|
1020
|
+
mount(target: string | HTMLElement): void;
|
|
1021
|
+
/**
|
|
1022
|
+
* Warm model (and optionally camera). Default coldStart = basic (no gUM).
|
|
1023
|
+
* Alias: {@link warmup}.
|
|
1024
|
+
*/
|
|
1025
|
+
preload(options?: PreloadOptions): Promise<void>;
|
|
1026
|
+
warmup(options?: PreloadOptions): Promise<void>;
|
|
1027
|
+
/** Mount + load model + open camera + start loop (camera-screen convenience). */
|
|
1028
|
+
start(): Promise<void>;
|
|
1029
|
+
stop(): void;
|
|
1030
|
+
dispose(): Promise<void>;
|
|
1031
|
+
configure(apiToken?: string): Promise<boolean>;
|
|
1032
|
+
getAvailableExercises(): ExerciseConfig[];
|
|
1033
|
+
getAvailableCustomExercises(): CustomExerciseDescriptor[];
|
|
1034
|
+
startExercise(exerciseId: string, options?: StartExerciseOptions): void;
|
|
1035
|
+
private startCustomExercise;
|
|
1036
|
+
stopExercise(): void;
|
|
1037
|
+
getCurrentExerciseId(): string | null;
|
|
1038
|
+
fetchSkeleton(uuid: string): Promise<SkeletonDefinition>;
|
|
1039
|
+
setModel(model?: PoseModelAlias, modelUrl?: string): Promise<void>;
|
|
1040
|
+
updateOptions(partial: Partial<PoseTrackerClientOptions>): void;
|
|
1041
|
+
ingestPose(pose: Pose): void;
|
|
1042
|
+
private doPreload;
|
|
1043
|
+
private ensureColdStartMode;
|
|
1044
|
+
private ensureAdapter;
|
|
1045
|
+
private ensureCamera;
|
|
1046
|
+
private beginLoop;
|
|
1047
|
+
private loop;
|
|
1048
|
+
private resolveSkeletonFromOptions;
|
|
1049
|
+
private resolveManifestOnce;
|
|
1050
|
+
private doResolveManifestOnce;
|
|
1051
|
+
private handleRevocation;
|
|
1052
|
+
private handleCameraStart;
|
|
1053
|
+
private retryMeteredSessionIfRefused;
|
|
1054
|
+
private tryConfigure;
|
|
1055
|
+
private resolveManifest;
|
|
1056
|
+
private validateRequestedFeatures;
|
|
1057
|
+
private emitFromSession;
|
|
1058
|
+
private watermarkOn;
|
|
1059
|
+
private applyWatermark;
|
|
1060
|
+
private revealCamera;
|
|
1061
|
+
private setStatus;
|
|
1062
|
+
private setMode;
|
|
1063
|
+
private reportError;
|
|
1064
|
+
private notifyState;
|
|
1065
|
+
private emit;
|
|
1066
|
+
}
|
|
1067
|
+
/** Factory — `createPoseTracker({ model: 'movenet' })`. */
|
|
1068
|
+
declare function createPoseTracker(options?: PoseTrackerClientOptions): PoseTrackerClient;
|
|
1069
|
+
declare function PoseCamera_mount(target: string | HTMLElement, options?: PoseTrackerClientOptions): PoseTrackerClient;
|
|
1070
|
+
declare const PoseCamera: {
|
|
1071
|
+
mount: typeof PoseCamera_mount;
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
interface Letterbox {
|
|
1075
|
+
offsetX: number;
|
|
1076
|
+
offsetY: number;
|
|
1077
|
+
drawW: number;
|
|
1078
|
+
drawH: number;
|
|
1079
|
+
vw: number;
|
|
1080
|
+
vh: number;
|
|
1081
|
+
}
|
|
1082
|
+
interface DetectorFrameResult {
|
|
1083
|
+
/** Raw model output used for skeleton mapping (MoveNet: Float32Array length 51). */
|
|
1084
|
+
raw?: Float32Array | Float32ArrayLike;
|
|
1085
|
+
keypoints: Keypoint[];
|
|
1086
|
+
score: number;
|
|
1087
|
+
inferenceMs: number;
|
|
1088
|
+
/** Video-space keypoints for overlay (before object-fit:cover). */
|
|
1089
|
+
videoKeypoints: Array<{
|
|
1090
|
+
name: string;
|
|
1091
|
+
xPx: number;
|
|
1092
|
+
yPx: number;
|
|
1093
|
+
score: number;
|
|
1094
|
+
}>;
|
|
1095
|
+
letterbox: Letterbox;
|
|
1096
|
+
}
|
|
1097
|
+
type Float32ArrayLike = ArrayLike<number>;
|
|
1098
|
+
interface PoseDetectorAdapter {
|
|
1099
|
+
readonly modelId: string;
|
|
1100
|
+
load(): Promise<void>;
|
|
1101
|
+
/**
|
|
1102
|
+
* Run one frame. `video` must have non-zero dimensions.
|
|
1103
|
+
* Returns null when the frame should be skipped.
|
|
1104
|
+
*/
|
|
1105
|
+
estimate(video: HTMLVideoElement, options: {
|
|
1106
|
+
facingMode: 'user' | 'environment';
|
|
1107
|
+
displayWidth: number;
|
|
1108
|
+
displayHeight: number;
|
|
1109
|
+
}): Promise<DetectorFrameResult | null>;
|
|
1110
|
+
dispose(): void;
|
|
1111
|
+
}
|
|
1112
|
+
interface CreateAdapterContext {
|
|
1113
|
+
resolved: ResolvedPoseModel;
|
|
1114
|
+
/** Optional override when host already loaded tf. */
|
|
1115
|
+
getTf?: () => typeof _tensorflow_tfjs;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
declare function createPoseAdapter(ctx: CreateAdapterContext): PoseDetectorAdapter;
|
|
1119
|
+
|
|
1120
|
+
declare class MoveNetGraphAdapter implements PoseDetectorAdapter {
|
|
1121
|
+
readonly modelId: string;
|
|
1122
|
+
private readonly modelUrl;
|
|
1123
|
+
private tf;
|
|
1124
|
+
private model;
|
|
1125
|
+
private backend;
|
|
1126
|
+
private offscreen;
|
|
1127
|
+
private offCtx;
|
|
1128
|
+
private smoothed;
|
|
1129
|
+
private letterbox;
|
|
1130
|
+
constructor(ctx: CreateAdapterContext);
|
|
1131
|
+
getBackend(): string | null;
|
|
1132
|
+
load(): Promise<void>;
|
|
1133
|
+
private poseCanvasCtx;
|
|
1134
|
+
private prepareInput;
|
|
1135
|
+
private smooth;
|
|
1136
|
+
estimate(video: HTMLVideoElement, options: {
|
|
1137
|
+
facingMode: 'user' | 'environment';
|
|
1138
|
+
displayWidth: number;
|
|
1139
|
+
displayHeight: number;
|
|
1140
|
+
}): Promise<DetectorFrameResult | null>;
|
|
1141
|
+
dispose(): void;
|
|
1142
|
+
}
|
|
1143
|
+
/** Map video keypoints → object-fit:cover display pixels for drawing. */
|
|
1144
|
+
declare function mapVideoKeypointsToDisplay(videoKeypoints: Array<{
|
|
1145
|
+
name: string;
|
|
1146
|
+
xPx: number;
|
|
1147
|
+
yPx: number;
|
|
1148
|
+
score: number;
|
|
1149
|
+
}>, letterbox: Letterbox, dispW: number, dispH: number): Array<{
|
|
1150
|
+
name: string;
|
|
1151
|
+
dx: number;
|
|
1152
|
+
dy: number;
|
|
1153
|
+
score: number;
|
|
1154
|
+
}>;
|
|
1155
|
+
|
|
1156
|
+
type BlazePoseModelType = 'lite' | 'full' | 'heavy';
|
|
1157
|
+
declare class BlazePoseAdapter implements PoseDetectorAdapter {
|
|
1158
|
+
readonly modelId = "blazepose";
|
|
1159
|
+
private detector;
|
|
1160
|
+
private backend;
|
|
1161
|
+
private readonly modelType;
|
|
1162
|
+
constructor(_ctx: CreateAdapterContext);
|
|
1163
|
+
getBackend(): string | null;
|
|
1164
|
+
load(): Promise<void>;
|
|
1165
|
+
estimate(video: HTMLVideoElement, options: {
|
|
1166
|
+
facingMode: 'user' | 'environment';
|
|
1167
|
+
displayWidth: number;
|
|
1168
|
+
displayHeight: number;
|
|
1169
|
+
}): Promise<DetectorFrameResult | null>;
|
|
1170
|
+
dispose(): void;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* Resolve TensorFlow.js: peer dependency (bundlers) or global `tf` (CDN script tags).
|
|
1175
|
+
*/
|
|
1176
|
+
declare const ONLINE_TFJS_VERSION = "4.22.0";
|
|
1177
|
+
interface TfjsCdnUrls {
|
|
1178
|
+
scriptUrls: string[];
|
|
1179
|
+
wasmPath: string;
|
|
1180
|
+
}
|
|
1181
|
+
declare function defaultTfjsCdnUrls(cdnBase?: string, tfjsVersion?: string): TfjsCdnUrls;
|
|
1182
|
+
type TfModule = typeof _tensorflow_tfjs;
|
|
1183
|
+
declare global {
|
|
1184
|
+
interface Window {
|
|
1185
|
+
tf?: TfModule;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Load TF.js from `window.tf` (CDN) or npm peer import.
|
|
1190
|
+
*/
|
|
1191
|
+
declare function resolveTf(): Promise<TfModule>;
|
|
1192
|
+
declare function initWebGlBackend(tf: TfModule): Promise<'webgl' | 'cpu'>;
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* Resolve `@tensorflow-models/pose-detection` for BlazePose:
|
|
1196
|
+
* 1. Global `window.poseDetection` (CDN / IIFE)
|
|
1197
|
+
* 2. Optional npm peer (bundlers)
|
|
1198
|
+
* 3. Dynamic jsDelivr script inject in the browser when peer is missing
|
|
1199
|
+
*/
|
|
1200
|
+
declare const ONLINE_POSE_DETECTION_VERSION = "2.1.3";
|
|
1201
|
+
type PoseDetectionModule = {
|
|
1202
|
+
SupportedModels: {
|
|
1203
|
+
BlazePose: string;
|
|
1204
|
+
};
|
|
1205
|
+
createDetector: (model: string, config?: Record<string, unknown>) => Promise<{
|
|
1206
|
+
estimatePoses: (input: HTMLVideoElement, opts?: Record<string, unknown>) => Promise<Array<{
|
|
1207
|
+
keypoints: Array<{
|
|
1208
|
+
name?: string;
|
|
1209
|
+
x: number;
|
|
1210
|
+
y: number;
|
|
1211
|
+
score?: number;
|
|
1212
|
+
}>;
|
|
1213
|
+
}>>;
|
|
1214
|
+
dispose?: () => void;
|
|
1215
|
+
}>;
|
|
1216
|
+
};
|
|
1217
|
+
declare global {
|
|
1218
|
+
interface Window {
|
|
1219
|
+
poseDetection?: PoseDetectionModule;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
declare function defaultPoseDetectionCdnUrl(cdnBase?: string, version?: string): string;
|
|
1223
|
+
/**
|
|
1224
|
+
* Load pose-detection from `window.poseDetection`, npm peer, or CDN inject.
|
|
1225
|
+
*/
|
|
1226
|
+
declare function resolvePoseDetection(options?: {
|
|
1227
|
+
cdnBase?: string;
|
|
1228
|
+
version?: string;
|
|
1229
|
+
/** Skip CDN inject (tests / Node). Default false. */
|
|
1230
|
+
allowCdn?: boolean;
|
|
1231
|
+
}): Promise<PoseDetectionModule>;
|
|
1232
|
+
|
|
1233
|
+
interface CameraOptions {
|
|
1234
|
+
facingMode?: 'user' | 'environment';
|
|
1235
|
+
idealWidth?: number;
|
|
1236
|
+
idealHeight?: number;
|
|
1237
|
+
idealFrameRate?: number;
|
|
1238
|
+
}
|
|
1239
|
+
declare function openCameraStream(options?: CameraOptions): Promise<MediaStream>;
|
|
1240
|
+
declare function stopMediaStream(stream: MediaStream | null | undefined): void;
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Brand assets for boot overlay / watermark.
|
|
1244
|
+
* Remote logo keeps the npm package small (light philosophy).
|
|
1245
|
+
*/
|
|
1246
|
+
declare const DEFAULT_LOADING_TEXT = "AI Loading";
|
|
1247
|
+
/** Public PoseTracker logo (docs site). Falls back to text if offline. */
|
|
1248
|
+
declare const POSETRACKER_LOGO_URL = "https://docs.posetracker.com/logo/light.png";
|
|
1249
|
+
declare const BRAND_COLORS: {
|
|
1250
|
+
readonly navy: "#010A73";
|
|
1251
|
+
readonly gold: "#FFC300";
|
|
1252
|
+
readonly bootBg: "#0a0a0a";
|
|
1253
|
+
readonly logoPad: "#3a3a3a";
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
/** @deprecated Prefer DEFAULT_SKELETON_DEFINITION */
|
|
1257
|
+
declare const DEFAULT_SKELETON: SkeletonDefinition;
|
|
1258
|
+
interface DrawKeypoint {
|
|
1259
|
+
name: string;
|
|
1260
|
+
dx: number;
|
|
1261
|
+
dy: number;
|
|
1262
|
+
score: number;
|
|
1263
|
+
}
|
|
1264
|
+
declare function drawSkeleton(ctx: CanvasRenderingContext2D, kps: DrawKeypoint[], dispW: number, dispH: number, enabled?: boolean, def?: SkeletonDefinition | null): void;
|
|
1265
|
+
|
|
1266
|
+
interface DomShell {
|
|
1267
|
+
root: HTMLElement;
|
|
1268
|
+
video: HTMLVideoElement;
|
|
1269
|
+
canvas: HTMLCanvasElement;
|
|
1270
|
+
boot: HTMLElement;
|
|
1271
|
+
bootMsg: HTMLElement;
|
|
1272
|
+
watermark: HTMLElement;
|
|
1273
|
+
hud: HTMLElement;
|
|
1274
|
+
placement: HTMLElement;
|
|
1275
|
+
setBootMessage(text: string, isError?: boolean): void;
|
|
1276
|
+
setCameraVisible(visible: boolean): void;
|
|
1277
|
+
setWatermarkVisible(visible: boolean): void;
|
|
1278
|
+
setHud(text: string, debug: boolean): void;
|
|
1279
|
+
setPlacementVisible(visible: boolean, paddingPercent?: number): void;
|
|
1280
|
+
applyMirror(facingMode: 'user' | 'environment'): void;
|
|
1281
|
+
destroy(): void;
|
|
1282
|
+
}
|
|
1283
|
+
declare function mountDomShell(host: HTMLElement, options?: {
|
|
1284
|
+
loadingText?: string;
|
|
1285
|
+
logoUrl?: string;
|
|
1286
|
+
}): DomShell;
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Fetch a custom skeleton overlay by Strapi `api_uuid`
|
|
1290
|
+
* (WebView `?skeleton=<uuid>` parity).
|
|
1291
|
+
*/
|
|
1292
|
+
|
|
1293
|
+
interface FetchSkeletonOptions {
|
|
1294
|
+
baseUrl?: string;
|
|
1295
|
+
}
|
|
1296
|
+
declare class SkeletonFetchError extends Error {
|
|
1297
|
+
readonly code: 'not_found' | 'network' | 'invalid';
|
|
1298
|
+
constructor(code: 'not_found' | 'network' | 'invalid', message: string);
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* GET /api/sdk/skeleton?uuid=…
|
|
1302
|
+
* Returns the overlay definition or throws {@link SkeletonFetchError}.
|
|
1303
|
+
*/
|
|
1304
|
+
declare function fetchSkeletonDefinition(uuid: string, options?: FetchSkeletonOptions): Promise<SkeletonDefinition>;
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* V2 Strapi movement names → canonical V3 exercise keys.
|
|
1308
|
+
* Mirrors PoseTrackerFront/lib/v3/exercises/index.js `EXERCISE_ALIASES`.
|
|
1309
|
+
*
|
|
1310
|
+
* The RN SDK manifest still often ships legacy `face_*` ids from Strapi while
|
|
1311
|
+
* host apps / demos call V3 keys (`squat`, `push_up`, …).
|
|
1312
|
+
*/
|
|
1313
|
+
declare const EXERCISE_ALIASES: Readonly<Record<string, string>>;
|
|
1314
|
+
/** Resolve a legacy or V3 id to the canonical V3 key. */
|
|
1315
|
+
declare function resolveExerciseName(name: string): string;
|
|
1316
|
+
/**
|
|
1317
|
+
* Pick the best matching exercise config for a requested id.
|
|
1318
|
+
* Tries exact id, then any legacy alias that maps to the same V3 key,
|
|
1319
|
+
* then any config whose id resolves to the same V3 key.
|
|
1320
|
+
*/
|
|
1321
|
+
declare function findExerciseByIdOrAlias<T extends {
|
|
1322
|
+
id: string;
|
|
1323
|
+
}>(exerciseId: string, exercises: readonly T[]): T | undefined;
|
|
1324
|
+
|
|
1325
|
+
export { type AccelerationDiagnostics, type AccelerationState, type AngleValue, type AnglesEvent, BRAND_COLORS, BlazePoseAdapter, type BlazePoseModelType, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, type CameraOptions, type ClassicMessageListener, type ClassicNativeMessage, type CocoKeypointName, type ColdStartMode, ConfigureError, type ConfigureOptions, type ConfigureRequest, type CounterEvent, type CounterFormScore, type CustomExerciseDescriptor, type CustomSessionOptions, DEFAULT_BASE_URL, DEFAULT_FEATURES, DEFAULT_LOADING_TEXT, DEFAULT_MOVENET_LIGHTNING_URL, DEFAULT_SKELETON, DEFAULT_SKELETON_DEFINITION, type DetectorFrameResult, type DiagnosticListener, EXERCISE_ALIASES, type EngineBundleDescriptor, type EngineLoadResult, EngineLoader, type EngineLoaderOptions, type EngineSession, type EngineSessionFeatures, type EngineSessionOptions, type ErrorEvent, type EventSink, type ExerciseConfig, type ExerciseSummaryEvent, FREE_PLAN_FEATURES_MESSAGE, type FetchSkeletonOptions, type FileStore, type FormGrade, type FormScoreEvent, INVALID_TOKEN_MESSAGE, type InferenceRuntime, type InitialPosture, type InitializationEvent, type InitializationStep, type JumpCalibrationEvent, type JumpDiscardedEvent, type JumpHeightEvent, type JumpResultEvent, type JumpStartedEvent, type JumpSummaryEntry, type JumpSummaryEvent, type KeyValueStore, type Keypoint, type KeypointName, type KeypointsEvent, type MinGrade, type ModelDescriptor, type ModelFormat, type ModelsByProfile, MoveNetGraphAdapter, type MovementDefinition, type MovementRecommendation, type MovementStep, type MovementStepParameter, ONLINE_POSE_DETECTION_VERSION, ONLINE_TFJS_VERSION, POSETRACKER_LOGO_URL, type PerformanceWarningEvent, type PlanInfo, type Pose, PoseCamera, PoseCamera_mount, type PoseDetectionModule, type PoseDetectorAdapter, type PoseModelAlias, type PoseModelKind, type PoseModelProfile, type PoseRuntimeDescriptor, type PoseRuntimePartDescriptor, type PoseTracker, type PoseTrackerCallbacks, PoseTrackerClient, type PoseTrackerClientOptions, type PoseTrackerEngine, type PoseTrackerEvent, type PoseTrackerEventListener, type PoseTrackerEventType, type PoseTrackerFeatures, type PoseTrackerMode, type PoseTrackerOptions, type PoseTrackerStatus, type PostureEvent, type PreloadOptions, type ProgressionEvent, type QualityChangedEvent, type RecommendationsEvent, type ReferenceMovementDescriptor, type RepSummary, type ResolvePoseModelOptions, type ResolvedFeatures, type ResolvedPoseModel, type RuntimeDownloadProgressEvent, SDK_NAME, SDK_VERSION, type SdkManifest, type SkeletonAnglesStyle, type SkeletonCirclesStyle, type SkeletonDefinition, SkeletonFetchError, type SkeletonLinesStyle, type StartExerciseOptions, type StatsEvent, type TfModule, type TfjsCdnUrls, TrackError, type TrackRequest, type TrackResponse, UsageTracker, type UsageTrackerOptions, type VisibleHipsInfo, type WarningEvent, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, mapVideoKeypointsToDisplay, mountDomShell, openCameraStream, resolveExerciseName, resolveFeatures, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|