@pose-tracker/pose-estimation-web 0.3.0 → 0.3.1
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.cjs +34 -6
- package/dist/index.d.cts +33 -8
- package/dist/index.d.ts +33 -8
- package/dist/index.js +32 -7
- package/dist/pose-tracker.global.js +3 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var jsSha256 = require('js-sha256');
|
|
4
4
|
|
|
5
5
|
// src/version.ts
|
|
6
|
-
var SDK_VERSION = "0.3.
|
|
6
|
+
var SDK_VERSION = "0.3.1";
|
|
7
7
|
var SDK_NAME = "posetracker-web";
|
|
8
8
|
|
|
9
9
|
// src/api/configure.ts
|
|
@@ -25,7 +25,7 @@ async function configure(apiToken, options = {}) {
|
|
|
25
25
|
poseModelProfile: options.poseModelProfile ?? "AdaptiveChoice",
|
|
26
26
|
locale: options.locale,
|
|
27
27
|
localVersions: options.localVersions,
|
|
28
|
-
engineChannel: options.engine === "
|
|
28
|
+
engineChannel: options.engine === "v3" ? "v3" : "v4"
|
|
29
29
|
};
|
|
30
30
|
let response;
|
|
31
31
|
try {
|
|
@@ -623,10 +623,22 @@ function findExerciseByIdOrAlias(exerciseId, exercises) {
|
|
|
623
623
|
var V4_ONLY_EXERCISE_IDS = [
|
|
624
624
|
"shoulder_roll",
|
|
625
625
|
"shoulder_deep_breath",
|
|
626
|
-
"chair_forward_fold"
|
|
626
|
+
"chair_forward_fold",
|
|
627
|
+
"chair_side_stretch"
|
|
627
628
|
];
|
|
629
|
+
var PRODUCTION_SQUAT_IDS = ["squat", "face_squat"];
|
|
630
|
+
function isProductionSquatId(exerciseId) {
|
|
631
|
+
const id = String(exerciseId || "").trim().toLowerCase();
|
|
632
|
+
return id === "squat" || id === "face_squat";
|
|
633
|
+
}
|
|
628
634
|
function normalizeEngineChannel(raw) {
|
|
629
|
-
return raw === "
|
|
635
|
+
return raw === "v3" ? "v3" : "v4";
|
|
636
|
+
}
|
|
637
|
+
function resolveMovementEngine(opts = {}) {
|
|
638
|
+
if (opts.userExercise) return "v4";
|
|
639
|
+
const requested = String(opts.engine || "").trim().toLowerCase();
|
|
640
|
+
if (isProductionSquatId(opts.exercise) && requested !== "v4") return "v3";
|
|
641
|
+
return normalizeEngineChannel(opts.engine);
|
|
630
642
|
}
|
|
631
643
|
function requiresEngineV4(exerciseId) {
|
|
632
644
|
return V4_ONLY_EXERCISE_IDS.includes(exerciseId);
|
|
@@ -2330,9 +2342,14 @@ var PoseTrackerClient = class {
|
|
|
2330
2342
|
this.reportError({ type: "error", code: "invalid_exercise", message });
|
|
2331
2343
|
throw new Error(message);
|
|
2332
2344
|
}
|
|
2333
|
-
|
|
2345
|
+
const jumpId = resolveExerciseName(exerciseId) || exerciseId;
|
|
2346
|
+
const sessionEngine = resolveMovementEngine({
|
|
2347
|
+
engine: this.opts.engine,
|
|
2348
|
+
exercise: jumpId
|
|
2349
|
+
});
|
|
2350
|
+
if (sessionEngine === "v4") {
|
|
2334
2351
|
const listed = this.engine.listExercises?.() ?? [];
|
|
2335
|
-
const v4Hit = listed.find((e) => e.id === exerciseId);
|
|
2352
|
+
const v4Hit = listed.find((e) => e.id === exerciseId) ?? listed.find((e) => e.id === jumpId);
|
|
2336
2353
|
if (v4Hit) {
|
|
2337
2354
|
this.beginEngineSession(this.v4ExerciseConfig(v4Hit), options);
|
|
2338
2355
|
return;
|
|
@@ -2347,6 +2364,13 @@ var PoseTrackerClient = class {
|
|
|
2347
2364
|
this.reportError({ type: "error", code: "invalid_exercise", message });
|
|
2348
2365
|
throw new Error(message);
|
|
2349
2366
|
}
|
|
2367
|
+
if (isProductionSquatId(jumpId) && normalizeEngineChannel(this.opts.engine) === "v4") {
|
|
2368
|
+
this.beginEngineSession(
|
|
2369
|
+
this.v4ExerciseConfig({ id: jumpId || "squat", displayName: "Squat", type: "dynamic" }),
|
|
2370
|
+
options
|
|
2371
|
+
);
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2350
2374
|
const available = this.getAvailableExercises();
|
|
2351
2375
|
const exercise = findExerciseByIdOrAlias(exerciseId, available);
|
|
2352
2376
|
if (!exercise) {
|
|
@@ -2373,6 +2397,7 @@ var PoseTrackerClient = class {
|
|
|
2373
2397
|
locale: this.opts.locale ?? "en",
|
|
2374
2398
|
difficulty: options.difficulty,
|
|
2375
2399
|
minGrade: this.features.minGrade ?? void 0,
|
|
2400
|
+
v4CatalogSquat: this.opts.engine === "v4",
|
|
2376
2401
|
features: {
|
|
2377
2402
|
angles: this.features.angles,
|
|
2378
2403
|
recommendations: this.features.recommendations,
|
|
@@ -3210,6 +3235,7 @@ exports.MoveNetGraphAdapter = MoveNetGraphAdapter;
|
|
|
3210
3235
|
exports.ONLINE_POSE_DETECTION_VERSION = ONLINE_POSE_DETECTION_VERSION;
|
|
3211
3236
|
exports.ONLINE_TFJS_VERSION = ONLINE_TFJS_VERSION;
|
|
3212
3237
|
exports.POSETRACKER_LOGO_URL = POSETRACKER_LOGO_URL;
|
|
3238
|
+
exports.PRODUCTION_SQUAT_IDS = PRODUCTION_SQUAT_IDS;
|
|
3213
3239
|
exports.PoseCamera = PoseCamera;
|
|
3214
3240
|
exports.PoseCamera_mount = PoseCamera_mount;
|
|
3215
3241
|
exports.PoseTrackerClient = PoseTrackerClient;
|
|
@@ -3235,6 +3261,7 @@ exports.findExerciseByIdOrAlias = findExerciseByIdOrAlias;
|
|
|
3235
3261
|
exports.freeBlockedFeatures = freeBlockedFeatures;
|
|
3236
3262
|
exports.initWebGlBackend = initWebGlBackend;
|
|
3237
3263
|
exports.isPaidPlan = isPaidPlan;
|
|
3264
|
+
exports.isProductionSquatId = isProductionSquatId;
|
|
3238
3265
|
exports.mapVideoKeypointsToDisplay = mapVideoKeypointsToDisplay;
|
|
3239
3266
|
exports.mountDomShell = mountDomShell;
|
|
3240
3267
|
exports.normalizeEngineChannel = normalizeEngineChannel;
|
|
@@ -3243,6 +3270,7 @@ exports.openCameraStream = openCameraStream;
|
|
|
3243
3270
|
exports.requiresEngineV4 = requiresEngineV4;
|
|
3244
3271
|
exports.resolveExerciseName = resolveExerciseName;
|
|
3245
3272
|
exports.resolveFeatures = resolveFeatures;
|
|
3273
|
+
exports.resolveMovementEngine = resolveMovementEngine;
|
|
3246
3274
|
exports.resolvePoseDetection = resolvePoseDetection;
|
|
3247
3275
|
exports.resolvePoseModel = resolvePoseModel;
|
|
3248
3276
|
exports.resolveTf = resolveTf;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tensorflow_tfjs from '@tensorflow/tfjs';
|
|
2
2
|
|
|
3
3
|
/** Bumped with package.json version (keep in sync). */
|
|
4
|
-
declare const SDK_VERSION = "0.3.
|
|
4
|
+
declare const SDK_VERSION = "0.3.1";
|
|
5
5
|
declare const SDK_NAME: "posetracker-web";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -175,8 +175,8 @@ interface ConfigureRequest {
|
|
|
175
175
|
engine?: string | null;
|
|
176
176
|
};
|
|
177
177
|
/**
|
|
178
|
-
* Which remote engine to download.
|
|
179
|
-
*
|
|
178
|
+
* Which remote engine to download. New SDKs send `'v4'` by default.
|
|
179
|
+
* Omitted (0.2.x) still means V3 on the server. `'v3'` forces the FSM bundle.
|
|
180
180
|
*/
|
|
181
181
|
engineChannel?: 'v3' | 'v4';
|
|
182
182
|
}
|
|
@@ -209,11 +209,30 @@ interface SdkManifest {
|
|
|
209
209
|
expiresAt: number;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
/**
|
|
212
|
+
/**
|
|
213
|
+
* Engine channel + production squat lock.
|
|
214
|
+
*
|
|
215
|
+
* Default handshake is V4 except unlabeled squat / face_squat (V3 FSM).
|
|
216
|
+
* Explicit `engine: 'v4'` is the only way to run the V4 catalog squat.
|
|
217
|
+
* Explicit `engine: 'v3'` keeps the full V3 bundle.
|
|
218
|
+
* See posetracker-rn-sdk/engine/v4/catalog/SQUAT_PRODUCTION.md
|
|
219
|
+
*/
|
|
213
220
|
type EngineChannel = 'v3' | 'v4';
|
|
214
221
|
/** Movements that exist only in the V4 catalog (not in V3 FSM / Strapi). */
|
|
215
|
-
declare const V4_ONLY_EXERCISE_IDS: readonly ["shoulder_roll", "shoulder_deep_breath", "chair_forward_fold"];
|
|
222
|
+
declare const V4_ONLY_EXERCISE_IDS: readonly ["shoulder_roll", "shoulder_deep_breath", "chair_forward_fold", "chair_side_stretch"];
|
|
223
|
+
declare const PRODUCTION_SQUAT_IDS: readonly ["squat", "face_squat"];
|
|
224
|
+
declare function isProductionSquatId(exerciseId?: string | null): boolean;
|
|
225
|
+
/** Handshake default is V4. Pass `'v3'` to force the V3 bundle. */
|
|
216
226
|
declare function normalizeEngineChannel(raw?: string | null): EngineChannel;
|
|
227
|
+
/**
|
|
228
|
+
* Session interpreter for this id. Production squat stays V3 unless `engine`
|
|
229
|
+
* is explicitly `'v4'`. Other ids follow the handshake (default V4).
|
|
230
|
+
*/
|
|
231
|
+
declare function resolveMovementEngine(opts?: {
|
|
232
|
+
engine?: string | null;
|
|
233
|
+
exercise?: string | null;
|
|
234
|
+
userExercise?: boolean;
|
|
235
|
+
}): EngineChannel;
|
|
217
236
|
declare function requiresEngineV4(exerciseId: string): boolean;
|
|
218
237
|
|
|
219
238
|
/**
|
|
@@ -228,8 +247,9 @@ interface ConfigureOptions {
|
|
|
228
247
|
locale?: string;
|
|
229
248
|
localVersions?: ConfigureRequest['localVersions'];
|
|
230
249
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
250
|
+
* Remote engine channel. Default `'v4'`. Pass `'v3'` for the production FSM
|
|
251
|
+
* bundle. Unlabeled `startExercise('squat')` stays on the V3 squat FSM even
|
|
252
|
+
* on the V4 channel unless `engine` is explicitly `'v4'`.
|
|
233
253
|
*/
|
|
234
254
|
engine?: EngineChannel;
|
|
235
255
|
}
|
|
@@ -698,6 +718,11 @@ interface EngineSessionOptions {
|
|
|
698
718
|
minGrade?: MinGrade;
|
|
699
719
|
/** Emission flags — engines older than 1.1.0 ignore them (client re-filters). */
|
|
700
720
|
features?: EngineSessionFeatures;
|
|
721
|
+
/**
|
|
722
|
+
* Run the V4 catalog squat instead of the production V3 FSM.
|
|
723
|
+
* Set when the host passed `engine: 'v4'` explicitly.
|
|
724
|
+
*/
|
|
725
|
+
v4CatalogSquat?: boolean;
|
|
701
726
|
}
|
|
702
727
|
interface EngineSession {
|
|
703
728
|
/** Feed one estimated pose; the session emits events through the sink. */
|
|
@@ -1462,4 +1487,4 @@ declare function findExerciseByIdOrAlias<T extends {
|
|
|
1462
1487
|
id: string;
|
|
1463
1488
|
}>(exerciseId: string, exercises: readonly T[]): T | undefined;
|
|
1464
1489
|
|
|
1465
|
-
export { type AccelerationDiagnostics, type AccelerationState, type AngleValue, type AnglesEvent, BRAND_COLORS, BlazePoseAdapter, type BlazePoseModelType, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, type CameraFacingMode, type CameraOptions, type CameraPoseSource, 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 EngineChannel, 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 ImagePoseSource, 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 PoseEstimateInput, type PoseModelAlias, type PoseModelKind, type PoseModelProfile, type PoseRuntimeDescriptor, type PoseRuntimePartDescriptor, type PoseSource, type PoseSourceProps, type PoseSourceType, 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, V4_ONLY_EXERCISE_IDS, type VideoPoseSource, type VisibleHipsInfo, type WarningEvent, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, mapVideoKeypointsToDisplay, mountDomShell, normalizeEngineChannel, normalizePoseSource, openCameraStream, requiresEngineV4, resolveExerciseName, resolveFeatures, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|
|
1490
|
+
export { type AccelerationDiagnostics, type AccelerationState, type AngleValue, type AnglesEvent, BRAND_COLORS, BlazePoseAdapter, type BlazePoseModelType, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, type CameraFacingMode, type CameraOptions, type CameraPoseSource, 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 EngineChannel, 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 ImagePoseSource, 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, PRODUCTION_SQUAT_IDS, type PerformanceWarningEvent, type PlanInfo, type Pose, PoseCamera, PoseCamera_mount, type PoseDetectionModule, type PoseDetectorAdapter, type PoseEstimateInput, type PoseModelAlias, type PoseModelKind, type PoseModelProfile, type PoseRuntimeDescriptor, type PoseRuntimePartDescriptor, type PoseSource, type PoseSourceProps, type PoseSourceType, 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, V4_ONLY_EXERCISE_IDS, type VideoPoseSource, type VisibleHipsInfo, type WarningEvent, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, isProductionSquatId, mapVideoKeypointsToDisplay, mountDomShell, normalizeEngineChannel, normalizePoseSource, openCameraStream, requiresEngineV4, resolveExerciseName, resolveFeatures, resolveMovementEngine, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tensorflow_tfjs from '@tensorflow/tfjs';
|
|
2
2
|
|
|
3
3
|
/** Bumped with package.json version (keep in sync). */
|
|
4
|
-
declare const SDK_VERSION = "0.3.
|
|
4
|
+
declare const SDK_VERSION = "0.3.1";
|
|
5
5
|
declare const SDK_NAME: "posetracker-web";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -175,8 +175,8 @@ interface ConfigureRequest {
|
|
|
175
175
|
engine?: string | null;
|
|
176
176
|
};
|
|
177
177
|
/**
|
|
178
|
-
* Which remote engine to download.
|
|
179
|
-
*
|
|
178
|
+
* Which remote engine to download. New SDKs send `'v4'` by default.
|
|
179
|
+
* Omitted (0.2.x) still means V3 on the server. `'v3'` forces the FSM bundle.
|
|
180
180
|
*/
|
|
181
181
|
engineChannel?: 'v3' | 'v4';
|
|
182
182
|
}
|
|
@@ -209,11 +209,30 @@ interface SdkManifest {
|
|
|
209
209
|
expiresAt: number;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
/**
|
|
212
|
+
/**
|
|
213
|
+
* Engine channel + production squat lock.
|
|
214
|
+
*
|
|
215
|
+
* Default handshake is V4 except unlabeled squat / face_squat (V3 FSM).
|
|
216
|
+
* Explicit `engine: 'v4'` is the only way to run the V4 catalog squat.
|
|
217
|
+
* Explicit `engine: 'v3'` keeps the full V3 bundle.
|
|
218
|
+
* See posetracker-rn-sdk/engine/v4/catalog/SQUAT_PRODUCTION.md
|
|
219
|
+
*/
|
|
213
220
|
type EngineChannel = 'v3' | 'v4';
|
|
214
221
|
/** Movements that exist only in the V4 catalog (not in V3 FSM / Strapi). */
|
|
215
|
-
declare const V4_ONLY_EXERCISE_IDS: readonly ["shoulder_roll", "shoulder_deep_breath", "chair_forward_fold"];
|
|
222
|
+
declare const V4_ONLY_EXERCISE_IDS: readonly ["shoulder_roll", "shoulder_deep_breath", "chair_forward_fold", "chair_side_stretch"];
|
|
223
|
+
declare const PRODUCTION_SQUAT_IDS: readonly ["squat", "face_squat"];
|
|
224
|
+
declare function isProductionSquatId(exerciseId?: string | null): boolean;
|
|
225
|
+
/** Handshake default is V4. Pass `'v3'` to force the V3 bundle. */
|
|
216
226
|
declare function normalizeEngineChannel(raw?: string | null): EngineChannel;
|
|
227
|
+
/**
|
|
228
|
+
* Session interpreter for this id. Production squat stays V3 unless `engine`
|
|
229
|
+
* is explicitly `'v4'`. Other ids follow the handshake (default V4).
|
|
230
|
+
*/
|
|
231
|
+
declare function resolveMovementEngine(opts?: {
|
|
232
|
+
engine?: string | null;
|
|
233
|
+
exercise?: string | null;
|
|
234
|
+
userExercise?: boolean;
|
|
235
|
+
}): EngineChannel;
|
|
217
236
|
declare function requiresEngineV4(exerciseId: string): boolean;
|
|
218
237
|
|
|
219
238
|
/**
|
|
@@ -228,8 +247,9 @@ interface ConfigureOptions {
|
|
|
228
247
|
locale?: string;
|
|
229
248
|
localVersions?: ConfigureRequest['localVersions'];
|
|
230
249
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
250
|
+
* Remote engine channel. Default `'v4'`. Pass `'v3'` for the production FSM
|
|
251
|
+
* bundle. Unlabeled `startExercise('squat')` stays on the V3 squat FSM even
|
|
252
|
+
* on the V4 channel unless `engine` is explicitly `'v4'`.
|
|
233
253
|
*/
|
|
234
254
|
engine?: EngineChannel;
|
|
235
255
|
}
|
|
@@ -698,6 +718,11 @@ interface EngineSessionOptions {
|
|
|
698
718
|
minGrade?: MinGrade;
|
|
699
719
|
/** Emission flags — engines older than 1.1.0 ignore them (client re-filters). */
|
|
700
720
|
features?: EngineSessionFeatures;
|
|
721
|
+
/**
|
|
722
|
+
* Run the V4 catalog squat instead of the production V3 FSM.
|
|
723
|
+
* Set when the host passed `engine: 'v4'` explicitly.
|
|
724
|
+
*/
|
|
725
|
+
v4CatalogSquat?: boolean;
|
|
701
726
|
}
|
|
702
727
|
interface EngineSession {
|
|
703
728
|
/** Feed one estimated pose; the session emits events through the sink. */
|
|
@@ -1462,4 +1487,4 @@ declare function findExerciseByIdOrAlias<T extends {
|
|
|
1462
1487
|
id: string;
|
|
1463
1488
|
}>(exerciseId: string, exercises: readonly T[]): T | undefined;
|
|
1464
1489
|
|
|
1465
|
-
export { type AccelerationDiagnostics, type AccelerationState, type AngleValue, type AnglesEvent, BRAND_COLORS, BlazePoseAdapter, type BlazePoseModelType, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, type CameraFacingMode, type CameraOptions, type CameraPoseSource, 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 EngineChannel, 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 ImagePoseSource, 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 PoseEstimateInput, type PoseModelAlias, type PoseModelKind, type PoseModelProfile, type PoseRuntimeDescriptor, type PoseRuntimePartDescriptor, type PoseSource, type PoseSourceProps, type PoseSourceType, 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, V4_ONLY_EXERCISE_IDS, type VideoPoseSource, type VisibleHipsInfo, type WarningEvent, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, mapVideoKeypointsToDisplay, mountDomShell, normalizeEngineChannel, normalizePoseSource, openCameraStream, requiresEngineV4, resolveExerciseName, resolveFeatures, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|
|
1490
|
+
export { type AccelerationDiagnostics, type AccelerationState, type AngleValue, type AnglesEvent, BRAND_COLORS, BlazePoseAdapter, type BlazePoseModelType, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, type CameraFacingMode, type CameraOptions, type CameraPoseSource, 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 EngineChannel, 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 ImagePoseSource, 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, PRODUCTION_SQUAT_IDS, type PerformanceWarningEvent, type PlanInfo, type Pose, PoseCamera, PoseCamera_mount, type PoseDetectionModule, type PoseDetectorAdapter, type PoseEstimateInput, type PoseModelAlias, type PoseModelKind, type PoseModelProfile, type PoseRuntimeDescriptor, type PoseRuntimePartDescriptor, type PoseSource, type PoseSourceProps, type PoseSourceType, 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, V4_ONLY_EXERCISE_IDS, type VideoPoseSource, type VisibleHipsInfo, type WarningEvent, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, isProductionSquatId, mapVideoKeypointsToDisplay, mountDomShell, normalizeEngineChannel, normalizePoseSource, openCameraStream, requiresEngineV4, resolveExerciseName, resolveFeatures, resolveMovementEngine, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sha256 } from 'js-sha256';
|
|
2
2
|
|
|
3
3
|
// src/version.ts
|
|
4
|
-
var SDK_VERSION = "0.3.
|
|
4
|
+
var SDK_VERSION = "0.3.1";
|
|
5
5
|
var SDK_NAME = "posetracker-web";
|
|
6
6
|
|
|
7
7
|
// src/api/configure.ts
|
|
@@ -23,7 +23,7 @@ async function configure(apiToken, options = {}) {
|
|
|
23
23
|
poseModelProfile: options.poseModelProfile ?? "AdaptiveChoice",
|
|
24
24
|
locale: options.locale,
|
|
25
25
|
localVersions: options.localVersions,
|
|
26
|
-
engineChannel: options.engine === "
|
|
26
|
+
engineChannel: options.engine === "v3" ? "v3" : "v4"
|
|
27
27
|
};
|
|
28
28
|
let response;
|
|
29
29
|
try {
|
|
@@ -621,10 +621,22 @@ function findExerciseByIdOrAlias(exerciseId, exercises) {
|
|
|
621
621
|
var V4_ONLY_EXERCISE_IDS = [
|
|
622
622
|
"shoulder_roll",
|
|
623
623
|
"shoulder_deep_breath",
|
|
624
|
-
"chair_forward_fold"
|
|
624
|
+
"chair_forward_fold",
|
|
625
|
+
"chair_side_stretch"
|
|
625
626
|
];
|
|
627
|
+
var PRODUCTION_SQUAT_IDS = ["squat", "face_squat"];
|
|
628
|
+
function isProductionSquatId(exerciseId) {
|
|
629
|
+
const id = String(exerciseId || "").trim().toLowerCase();
|
|
630
|
+
return id === "squat" || id === "face_squat";
|
|
631
|
+
}
|
|
626
632
|
function normalizeEngineChannel(raw) {
|
|
627
|
-
return raw === "
|
|
633
|
+
return raw === "v3" ? "v3" : "v4";
|
|
634
|
+
}
|
|
635
|
+
function resolveMovementEngine(opts = {}) {
|
|
636
|
+
if (opts.userExercise) return "v4";
|
|
637
|
+
const requested = String(opts.engine || "").trim().toLowerCase();
|
|
638
|
+
if (isProductionSquatId(opts.exercise) && requested !== "v4") return "v3";
|
|
639
|
+
return normalizeEngineChannel(opts.engine);
|
|
628
640
|
}
|
|
629
641
|
function requiresEngineV4(exerciseId) {
|
|
630
642
|
return V4_ONLY_EXERCISE_IDS.includes(exerciseId);
|
|
@@ -2328,9 +2340,14 @@ var PoseTrackerClient = class {
|
|
|
2328
2340
|
this.reportError({ type: "error", code: "invalid_exercise", message });
|
|
2329
2341
|
throw new Error(message);
|
|
2330
2342
|
}
|
|
2331
|
-
|
|
2343
|
+
const jumpId = resolveExerciseName(exerciseId) || exerciseId;
|
|
2344
|
+
const sessionEngine = resolveMovementEngine({
|
|
2345
|
+
engine: this.opts.engine,
|
|
2346
|
+
exercise: jumpId
|
|
2347
|
+
});
|
|
2348
|
+
if (sessionEngine === "v4") {
|
|
2332
2349
|
const listed = this.engine.listExercises?.() ?? [];
|
|
2333
|
-
const v4Hit = listed.find((e) => e.id === exerciseId);
|
|
2350
|
+
const v4Hit = listed.find((e) => e.id === exerciseId) ?? listed.find((e) => e.id === jumpId);
|
|
2334
2351
|
if (v4Hit) {
|
|
2335
2352
|
this.beginEngineSession(this.v4ExerciseConfig(v4Hit), options);
|
|
2336
2353
|
return;
|
|
@@ -2345,6 +2362,13 @@ var PoseTrackerClient = class {
|
|
|
2345
2362
|
this.reportError({ type: "error", code: "invalid_exercise", message });
|
|
2346
2363
|
throw new Error(message);
|
|
2347
2364
|
}
|
|
2365
|
+
if (isProductionSquatId(jumpId) && normalizeEngineChannel(this.opts.engine) === "v4") {
|
|
2366
|
+
this.beginEngineSession(
|
|
2367
|
+
this.v4ExerciseConfig({ id: jumpId || "squat", displayName: "Squat", type: "dynamic" }),
|
|
2368
|
+
options
|
|
2369
|
+
);
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2348
2372
|
const available = this.getAvailableExercises();
|
|
2349
2373
|
const exercise = findExerciseByIdOrAlias(exerciseId, available);
|
|
2350
2374
|
if (!exercise) {
|
|
@@ -2371,6 +2395,7 @@ var PoseTrackerClient = class {
|
|
|
2371
2395
|
locale: this.opts.locale ?? "en",
|
|
2372
2396
|
difficulty: options.difficulty,
|
|
2373
2397
|
minGrade: this.features.minGrade ?? void 0,
|
|
2398
|
+
v4CatalogSquat: this.opts.engine === "v4",
|
|
2374
2399
|
features: {
|
|
2375
2400
|
angles: this.features.angles,
|
|
2376
2401
|
recommendations: this.features.recommendations,
|
|
@@ -3189,6 +3214,6 @@ var PoseCamera = {
|
|
|
3189
3214
|
mount: PoseCamera_mount
|
|
3190
3215
|
};
|
|
3191
3216
|
|
|
3192
|
-
export { BRAND_COLORS, BlazePoseAdapter, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, ConfigureError, DEFAULT_BASE_URL, DEFAULT_FEATURES, DEFAULT_LOADING_TEXT, DEFAULT_MOVENET_LIGHTNING_URL, DEFAULT_SKELETON, DEFAULT_SKELETON_DEFINITION, EXERCISE_ALIASES, EngineLoader, FREE_PLAN_FEATURES_MESSAGE, INVALID_TOKEN_MESSAGE, MoveNetGraphAdapter, ONLINE_POSE_DETECTION_VERSION, ONLINE_TFJS_VERSION, POSETRACKER_LOGO_URL, PoseCamera, PoseCamera_mount, PoseTrackerClient, SDK_NAME, SDK_VERSION, SkeletonFetchError, TrackError, UsageTracker, V4_ONLY_EXERCISE_IDS, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, mapVideoKeypointsToDisplay, mountDomShell, normalizeEngineChannel, normalizePoseSource, openCameraStream, requiresEngineV4, resolveExerciseName, resolveFeatures, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|
|
3217
|
+
export { BRAND_COLORS, BlazePoseAdapter, COCO_KEYPOINT_NAMES, COMBINED_REFERENCE_EXERCISE_MESSAGE, ConfigureError, DEFAULT_BASE_URL, DEFAULT_FEATURES, DEFAULT_LOADING_TEXT, DEFAULT_MOVENET_LIGHTNING_URL, DEFAULT_SKELETON, DEFAULT_SKELETON_DEFINITION, EXERCISE_ALIASES, EngineLoader, FREE_PLAN_FEATURES_MESSAGE, INVALID_TOKEN_MESSAGE, MoveNetGraphAdapter, ONLINE_POSE_DETECTION_VERSION, ONLINE_TFJS_VERSION, POSETRACKER_LOGO_URL, PRODUCTION_SQUAT_IDS, PoseCamera, PoseCamera_mount, PoseTrackerClient, SDK_NAME, SDK_VERSION, SkeletonFetchError, TrackError, UsageTracker, V4_ONLY_EXERCISE_IDS, configure, createLocalFileStore, createLocalKeyValueStore, createMemoryFileStore, createPoseAdapter, createPoseTracker, createWebFileStore, defaultPoseDetectionCdnUrl, defaultTfjsCdnUrls, drawSkeleton, featureNotSupportedMessage, fetchSkeletonDefinition, findExerciseByIdOrAlias, freeBlockedFeatures, initWebGlBackend, isPaidPlan, isProductionSquatId, mapVideoKeypointsToDisplay, mountDomShell, normalizeEngineChannel, normalizePoseSource, openCameraStream, requiresEngineV4, resolveExerciseName, resolveFeatures, resolveMovementEngine, resolvePoseDetection, resolvePoseModel, resolveTf, shouldShowWatermark, stopMediaStream, toClassicNativeMessage };
|
|
3193
3218
|
//# sourceMappingURL=index.js.map
|
|
3194
3219
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var PoseTracker=(()=>{var Pt=Object.create;var pe=Object.defineProperty;var Tt=Object.getOwnPropertyDescriptor;var Ft=Object.getOwnPropertyNames;var Ct=Object.getPrototypeOf,Lt=Object.prototype.hasOwnProperty;var lt=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var It=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),At=(t,e)=>{for(var r in e)pe(t,r,{get:e[r],enumerable:!0})},ct=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ft(e))!Lt.call(t,i)&&i!==r&&pe(t,i,{get:()=>e[i],enumerable:!(s=Tt(e,i))||s.enumerable});return t};var dt=(t,e,r)=>(r=t!=null?Pt(Ct(t)):{},ct(e||!t||!t.__esModule?pe(r,"default",{value:t,enumerable:!0}):r,t)),Rt=t=>ct(pe({},"__esModule",{value:!0}),t);var Ae=It((pr,me)=>{"use strict";(function(){"use strict";var t="input is invalid type",e=typeof window=="object",r=e?window:{};r.JS_SHA256_NO_WINDOW&&(e=!1);var s=!e&&typeof self=="object",i=!r.JS_SHA256_NO_NODE_JS&&typeof process=="object"&&process.versions&&process.versions.node&&process.type!="renderer";i?r=global:s&&(r=self);var n=!r.JS_SHA256_NO_COMMON_JS&&typeof me=="object"&&me.exports,u=typeof define=="function"&&define.amd,f=!r.JS_SHA256_NO_ARRAY_BUFFER&&typeof ArrayBuffer<"u",l="0123456789abcdef".split(""),x=[-2147483648,8388608,32768,128],v=[24,16,8,0],_=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],b=["hex","array","digest","arrayBuffer"],g=[];(r.JS_SHA256_NO_NODE_JS||!Array.isArray)&&(Array.isArray=function(o){return Object.prototype.toString.call(o)==="[object Array]"}),f&&(r.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW||!ArrayBuffer.isView)&&(ArrayBuffer.isView=function(o){return typeof o=="object"&&o.buffer&&o.buffer.constructor===ArrayBuffer});var S=function(o,d){return function(m){return new k(d,!0).update(m)[o]()}},w=function(o){var d=S("hex",o);i&&(d=T(d,o)),d.create=function(){return new k(o)},d.update=function(p){return d.create().update(p)};for(var m=0;m<b.length;++m){var a=b[m];d[a]=S(a,o)}return d},T=function(o,d){var m=lt("crypto"),a=lt("buffer").Buffer,p=d?"sha224":"sha256",h;a.from&&!r.JS_SHA256_NO_BUFFER_FROM?h=a.from:h=function(c){return new a(c)};var y=function(c){if(typeof c=="string")return m.createHash(p).update(c,"utf8").digest("hex");if(c==null)throw new Error(t);return c.constructor===ArrayBuffer&&(c=new Uint8Array(c)),Array.isArray(c)||ArrayBuffer.isView(c)||c.constructor===a?m.createHash(p).update(h(c)).digest("hex"):o(c)};return y},C=function(o,d){return function(m,a){return new I(m,d,!0).update(a)[o]()}},F=function(o){var d=C("hex",o);d.create=function(p){return new I(p,o)},d.update=function(p,h){return d.create(p).update(h)};for(var m=0;m<b.length;++m){var a=b[m];d[a]=C(a,o)}return d};function k(o,d){d?(g[0]=g[16]=g[1]=g[2]=g[3]=g[4]=g[5]=g[6]=g[7]=g[8]=g[9]=g[10]=g[11]=g[12]=g[13]=g[14]=g[15]=0,this.blocks=g):this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],o?(this.h0=3238371032,this.h1=914150663,this.h2=812702999,this.h3=4144912697,this.h4=4290775857,this.h5=1750603025,this.h6=1694076839,this.h7=3204075428):(this.h0=1779033703,this.h1=3144134277,this.h2=1013904242,this.h3=2773480762,this.h4=1359893119,this.h5=2600822924,this.h6=528734635,this.h7=1541459225),this.block=this.start=this.bytes=this.hBytes=0,this.finalized=this.hashed=!1,this.first=!0,this.is224=o}k.prototype.update=function(o){if(!this.finalized){var d,m=typeof o;if(m!=="string"){if(m==="object"){if(o===null)throw new Error(t);if(f&&o.constructor===ArrayBuffer)o=new Uint8Array(o);else if(!Array.isArray(o)&&(!f||!ArrayBuffer.isView(o)))throw new Error(t)}else throw new Error(t);d=!0}for(var a,p=0,h,y=o.length,c=this.blocks;p<y;){if(this.hashed&&(this.hashed=!1,c[0]=this.block,this.block=c[16]=c[1]=c[2]=c[3]=c[4]=c[5]=c[6]=c[7]=c[8]=c[9]=c[10]=c[11]=c[12]=c[13]=c[14]=c[15]=0),d)for(h=this.start;p<y&&h<64;++p)c[h>>>2]|=o[p]<<v[h++&3];else for(h=this.start;p<y&&h<64;++p)a=o.charCodeAt(p),a<128?c[h>>>2]|=a<<v[h++&3]:a<2048?(c[h>>>2]|=(192|a>>>6)<<v[h++&3],c[h>>>2]|=(128|a&63)<<v[h++&3]):a<55296||a>=57344?(c[h>>>2]|=(224|a>>>12)<<v[h++&3],c[h>>>2]|=(128|a>>>6&63)<<v[h++&3],c[h>>>2]|=(128|a&63)<<v[h++&3]):(a=65536+((a&1023)<<10|o.charCodeAt(++p)&1023),c[h>>>2]|=(240|a>>>18)<<v[h++&3],c[h>>>2]|=(128|a>>>12&63)<<v[h++&3],c[h>>>2]|=(128|a>>>6&63)<<v[h++&3],c[h>>>2]|=(128|a&63)<<v[h++&3]);this.lastByteIndex=h,this.bytes+=h-this.start,h>=64?(this.block=c[16],this.start=h-64,this.hash(),this.hashed=!0):this.start=h}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},k.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var o=this.blocks,d=this.lastByteIndex;o[16]=this.block,o[d>>>2]|=x[d&3],this.block=o[16],d>=56&&(this.hashed||this.hash(),o[0]=this.block,o[16]=o[1]=o[2]=o[3]=o[4]=o[5]=o[6]=o[7]=o[8]=o[9]=o[10]=o[11]=o[12]=o[13]=o[14]=o[15]=0),o[14]=this.hBytes<<3|this.bytes>>>29,o[15]=this.bytes<<3,this.hash()}},k.prototype.hash=function(){var o=this.h0,d=this.h1,m=this.h2,a=this.h3,p=this.h4,h=this.h5,y=this.h6,c=this.h7,E=this.blocks,P,R,D,H,M,B,V,Y,Le,Ie,ue;for(P=16;P<64;++P)M=E[P-15],R=(M>>>7|M<<25)^(M>>>18|M<<14)^M>>>3,M=E[P-2],D=(M>>>17|M<<15)^(M>>>19|M<<13)^M>>>10,E[P]=E[P-16]+R+E[P-7]+D<<0;for(ue=d&m,P=0;P<64;P+=4)this.first?(this.is224?(Y=300032,M=E[0]-1413257819,c=M-150054599<<0,a=M+24177077<<0):(Y=704751109,M=E[0]-210244248,c=M-1521486534<<0,a=M+143694565<<0),this.first=!1):(R=(o>>>2|o<<30)^(o>>>13|o<<19)^(o>>>22|o<<10),D=(p>>>6|p<<26)^(p>>>11|p<<21)^(p>>>25|p<<7),Y=o&d,H=Y^o&m^ue,V=p&h^~p&y,M=c+D+V+_[P]+E[P],B=R+H,c=a+M<<0,a=M+B<<0),R=(a>>>2|a<<30)^(a>>>13|a<<19)^(a>>>22|a<<10),D=(c>>>6|c<<26)^(c>>>11|c<<21)^(c>>>25|c<<7),Le=a&o,H=Le^a&d^Y,V=c&p^~c&h,M=y+D+V+_[P+1]+E[P+1],B=R+H,y=m+M<<0,m=M+B<<0,R=(m>>>2|m<<30)^(m>>>13|m<<19)^(m>>>22|m<<10),D=(y>>>6|y<<26)^(y>>>11|y<<21)^(y>>>25|y<<7),Ie=m&a,H=Ie^m&o^Le,V=y&c^~y&p,M=h+D+V+_[P+2]+E[P+2],B=R+H,h=d+M<<0,d=M+B<<0,R=(d>>>2|d<<30)^(d>>>13|d<<19)^(d>>>22|d<<10),D=(h>>>6|h<<26)^(h>>>11|h<<21)^(h>>>25|h<<7),ue=d&m,H=ue^d&a^Ie,V=h&y^~h&c,M=p+D+V+_[P+3]+E[P+3],B=R+H,p=o+M<<0,o=M+B<<0,this.chromeBugWorkAround=!0;this.h0=this.h0+o<<0,this.h1=this.h1+d<<0,this.h2=this.h2+m<<0,this.h3=this.h3+a<<0,this.h4=this.h4+p<<0,this.h5=this.h5+h<<0,this.h6=this.h6+y<<0,this.h7=this.h7+c<<0},k.prototype.hex=function(){this.finalize();var o=this.h0,d=this.h1,m=this.h2,a=this.h3,p=this.h4,h=this.h5,y=this.h6,c=this.h7,E=l[o>>>28&15]+l[o>>>24&15]+l[o>>>20&15]+l[o>>>16&15]+l[o>>>12&15]+l[o>>>8&15]+l[o>>>4&15]+l[o&15]+l[d>>>28&15]+l[d>>>24&15]+l[d>>>20&15]+l[d>>>16&15]+l[d>>>12&15]+l[d>>>8&15]+l[d>>>4&15]+l[d&15]+l[m>>>28&15]+l[m>>>24&15]+l[m>>>20&15]+l[m>>>16&15]+l[m>>>12&15]+l[m>>>8&15]+l[m>>>4&15]+l[m&15]+l[a>>>28&15]+l[a>>>24&15]+l[a>>>20&15]+l[a>>>16&15]+l[a>>>12&15]+l[a>>>8&15]+l[a>>>4&15]+l[a&15]+l[p>>>28&15]+l[p>>>24&15]+l[p>>>20&15]+l[p>>>16&15]+l[p>>>12&15]+l[p>>>8&15]+l[p>>>4&15]+l[p&15]+l[h>>>28&15]+l[h>>>24&15]+l[h>>>20&15]+l[h>>>16&15]+l[h>>>12&15]+l[h>>>8&15]+l[h>>>4&15]+l[h&15]+l[y>>>28&15]+l[y>>>24&15]+l[y>>>20&15]+l[y>>>16&15]+l[y>>>12&15]+l[y>>>8&15]+l[y>>>4&15]+l[y&15];return this.is224||(E+=l[c>>>28&15]+l[c>>>24&15]+l[c>>>20&15]+l[c>>>16&15]+l[c>>>12&15]+l[c>>>8&15]+l[c>>>4&15]+l[c&15]),E},k.prototype.toString=k.prototype.hex,k.prototype.digest=function(){this.finalize();var o=this.h0,d=this.h1,m=this.h2,a=this.h3,p=this.h4,h=this.h5,y=this.h6,c=this.h7,E=[o>>>24&255,o>>>16&255,o>>>8&255,o&255,d>>>24&255,d>>>16&255,d>>>8&255,d&255,m>>>24&255,m>>>16&255,m>>>8&255,m&255,a>>>24&255,a>>>16&255,a>>>8&255,a&255,p>>>24&255,p>>>16&255,p>>>8&255,p&255,h>>>24&255,h>>>16&255,h>>>8&255,h&255,y>>>24&255,y>>>16&255,y>>>8&255,y&255];return this.is224||E.push(c>>>24&255,c>>>16&255,c>>>8&255,c&255),E},k.prototype.array=k.prototype.digest,k.prototype.arrayBuffer=function(){this.finalize();var o=new ArrayBuffer(this.is224?28:32),d=new DataView(o);return d.setUint32(0,this.h0),d.setUint32(4,this.h1),d.setUint32(8,this.h2),d.setUint32(12,this.h3),d.setUint32(16,this.h4),d.setUint32(20,this.h5),d.setUint32(24,this.h6),this.is224||d.setUint32(28,this.h7),o};function I(o,d,m){var a,p=typeof o;if(p==="string"){var h=[],y=o.length,c=0,E;for(a=0;a<y;++a)E=o.charCodeAt(a),E<128?h[c++]=E:E<2048?(h[c++]=192|E>>>6,h[c++]=128|E&63):E<55296||E>=57344?(h[c++]=224|E>>>12,h[c++]=128|E>>>6&63,h[c++]=128|E&63):(E=65536+((E&1023)<<10|o.charCodeAt(++a)&1023),h[c++]=240|E>>>18,h[c++]=128|E>>>12&63,h[c++]=128|E>>>6&63,h[c++]=128|E&63);o=h}else if(p==="object"){if(o===null)throw new Error(t);if(f&&o.constructor===ArrayBuffer)o=new Uint8Array(o);else if(!Array.isArray(o)&&(!f||!ArrayBuffer.isView(o)))throw new Error(t)}else throw new Error(t);o.length>64&&(o=new k(d,!0).update(o).array());var P=[],R=[];for(a=0;a<64;++a){var D=o[a]||0;P[a]=92^D,R[a]=54^D}k.call(this,d,m),this.update(R),this.oKeyPad=P,this.inner=!0,this.sharedMemory=m}I.prototype=new k,I.prototype.finalize=function(){if(k.prototype.finalize.call(this),this.inner){this.inner=!1;var o=this.array();k.call(this,this.is224,this.sharedMemory),this.update(this.oKeyPad),this.update(o),k.prototype.finalize.call(this)}};var L=w();L.sha256=L,L.sha224=w(!0),L.sha256.hmac=F(),L.sha224.hmac=F(!0),n?me.exports=L:(r.sha256=L.sha256,r.sha224=L.sha224,u&&define(function(){return L}))})()});var lr={};At(lr,{COCO_KEYPOINT_NAMES:()=>N,DEFAULT_MOVENET_LIGHTNING_URL:()=>be,ONLINE_TFJS_VERSION:()=>ke,PoseCamera:()=>at,SDK_NAME:()=>X,SDK_VERSION:()=>K,configure:()=>G,createPoseTracker:()=>Ce,defaultTfjsCdnUrls:()=>Ye,resolvePoseModel:()=>W,shouldShowWatermark:()=>he});var K="0.3.0",X="posetracker-web";var j="https://movelytics-strapi-c78a339b7070.herokuapp.com",U=class extends Error{constructor(r,s){super(s);this.code=r;this.name="ConfigureError"}};async function G(t,e={}){let r=(e.baseUrl??j).replace(/\/$/,""),s={...t?{apiToken:t}:{},sdkName:X,sdkVersion:K,targetPlatform:"web",poseModelProfile:e.poseModelProfile??"AdaptiveChoice",locale:e.locale,localVersions:e.localVersions,engineChannel:e.engine==="v4"?"v4":"v3"},i;try{i=await fetch(`${r}/api/sdk/configure`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})}catch(n){throw new U("network",`Handshake request failed: ${String(n)}`)}if(i.status===401||i.status===403)throw new U("invalid_token","Invalid, revoked or unauthorized API token.");if(i.status===429)throw new U("quota_exceeded","API call quota exceeded for the current plan.");if(!i.ok)throw new U("internal",`Handshake failed with HTTP ${i.status}.`);return await i.json()}var Ne=dt(Ae(),1);var De=dt(Ae(),1),Re="ptc1:";function Oe(t){return(0,De.sha256)(`posetracker-sdk-cache:${t}`)}function Dt(t){let e=unescape(encodeURIComponent(t)),r=new Uint8Array(e.length);for(let s=0;s<e.length;s++)r[s]=e.charCodeAt(s);return r}function Ot(t){let e="";for(let r=0;r<t.length;r++)e+=String.fromCharCode(t[r]);return decodeURIComponent(escape(e))}function Ut(t){let e="";for(let r=0;r<t.length;r++)e+=t[r].toString(16).padStart(2,"0");return e}function Nt(t){if(t.length%2!==0||/[^0-9a-f]/.test(t))return null;let e=new Uint8Array(t.length/2);for(let r=0;r<e.length;r++)e[r]=parseInt(t.slice(r*2,r*2+2),16);return e}function ht(t,e){let r=new Uint8Array(t.length),s=0,i=0;for(;i<t.length;){let n=De.sha256.array(`${e}:${s++}`);for(let u=0;u<n.length&&i<t.length;u++,i++)r[i]=t[i]^n[u]}return r}function fe(t,e){return Re+Ut(ht(Dt(t),e))}function ge(t,e){if(!t.startsWith(Re))return null;let r=Nt(t.slice(Re.length));if(!r)return null;try{return Ot(ht(r,e))}catch{return null}}var Ue="posetracker.web.";function Q(){return typeof localStorage>"u"?null:{async getItem(t){try{return localStorage.getItem(Ue+t)}catch{return null}},async setItem(t,e){try{localStorage.setItem(Ue+t,e)}catch{}},async removeItem(t){try{localStorage.removeItem(Ue+t)}catch{}}}}function pt(){let t=Q();return t?{read:e=>t.getItem(`file.${e}`),write:(e,r)=>t.setItem(`file.${e}`,r),remove:e=>t.removeItem(`file.${e}`)}:null}function mt(){let t=new Map;return{async read(e){return t.has(e)?t.get(e):null},async write(e,r){t.set(e,r)},async remove(e){t.delete(e)}}}function ye(){return pt()??mt()}var ut="posetracker.engine.guard.",J=class{constructor(e={}){this.lastError=null;this.files=e.fileStore!==void 0?e.fileStore:ye(),this.kv=e.keyValueStore!==void 0?e.keyValueStore:Q(),this.fetchFn=e.fetchFn??fetch,this.onDiagnostic=e.onDiagnostic,this.onDiagnostic?.(`[posetracker-web] EngineLoader fileStore=${this.files?"yes":"none"} kv=${this.kv?"localStorage":"none"}`)}async clearGuard(e){if(!e||!this.kv)return;let r=`${ut}${e.version}.${e.sha256}`;await this.kv.removeItem(r).catch(()=>{})}async load(e,r,s){if(this.lastError=null,!e)return this.lastError="manifest has no engine descriptor",null;let i=`${ut}${e.version}.${e.sha256}`;s?.forceRetry&&await this.kv?.removeItem(i).catch(()=>{});let n=await this.kv?.getItem(i);if(n==="probing"||n==="failed")return await this.kv?.setItem(i,"failed"),this.lastError=`engine crash-guard blocked version=${e.version} (state=${n}) \u2014 call configure() again to force retry`,this.onDiagnostic?.(`[posetracker-web] ${this.lastError}`),null;let u=`engine-${e.version}.sealed`,f=await this.files?.read(u);if(f!=null){let l=ge(f,r);if(l!==null&&(0,Ne.sha256)(l)===e.sha256){let x=await this.evaluate(l,i);if(x)return this.onDiagnostic?.(`[posetracker-web] engine loaded from cache version=${e.version}`),{engine:x,source:"remote-cache"}}else this.onDiagnostic?.(`[posetracker-web] engine cache purged (unseal/integrity mismatch) key=${u}`),await this.files?.remove(u)}try{this.onDiagnostic?.(`[posetracker-web] downloading engine version=${e.version}\u2026`);let l=await this.fetchFn(e.signedUrl);if(!l.ok)throw new Error(`HTTP ${l.status}`);let x=await l.text(),v=(0,Ne.sha256)(x);if(v!==e.sha256)throw new Error(`integrity check failed (got ${v.slice(0,12)}\u2026 expected ${e.sha256.slice(0,12)}\u2026, bytes=${x.length})`);let _=await this.evaluate(x,i);if(_){try{await this.files?.write(u,fe(x,r))}catch(b){this.onDiagnostic?.("[posetracker-web] engine cache write failed (non-fatal): "+(b instanceof Error?b.message:String(b)))}return this.onDiagnostic?.(`[posetracker-web] engine loaded from download version=${e.version} bytes=${x.length}`),{engine:_,source:"remote-download"}}}catch(l){this.lastError=l instanceof Error?l.message:String(l),this.onDiagnostic?.(`[posetracker-web] engine download/load failed: ${this.lastError}`)}return this.lastError||(this.lastError="engine evaluate returned null"),null}async evaluate(e,r){await this.kv?.setItem(r,"probing");try{let s={exports:{}};new Function("module","exports",e)(s,s.exports);let n=s.exports.createEngine;if(typeof n!="function")throw new Error(`engine bundle does not export createEngine() (got ${typeof n}, keys=${Object.keys(s.exports).join(",")})`);let u=n();return await this.kv?.setItem(r,"passed"),u}catch(s){return this.lastError="engine evaluate failed: "+(s instanceof Error?s.message:String(s)),this.onDiagnostic?.(`[posetracker-web] ${this.lastError}`),await this.kv?.setItem(r,"failed"),null}}};var He="posetracker.track.queue",Ht=100,O=class extends Error{constructor(r,s){super(s);this.code=r;this.name="TrackError"}},Z=class{constructor(e={}){this.baseUrl=(e.baseUrl??j).replace(/\/$/,""),this.kv=e.keyValueStore!==void 0?e.keyValueStore:Q(),this.fetchFn=e.fetchFn??fetch}async trackMetered(e){return this.post({event:e.event??"camera_start",apiToken:e.apiToken,platform:"web",sdkVersion:K,params:e.params})}async trackAnonymous(e={}){let r={event:e.event??"camera_start",platform:"web",sdkVersion:K,params:e.params,queuedAtMs:Date.now()};try{await this.post({...r}),this.flushQueue()}catch{await this.enqueue(r)}}async flushQueue(){let e=await this.readQueue();if(e.length===0)return;let r=[];for(let s of e)try{await this.post({...s,params:{...s.params??{},queuedAtMs:s.queuedAtMs}})}catch{r.push(s)}await this.writeQueue(r)}async post(e){let r;try{r=await this.fetchFn(`${this.baseUrl}/api/sdk/track`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})}catch(s){throw new O("network",`Usage tracking request failed: ${String(s)}`)}if(r.status===401||r.status===403)throw new O("invalid_token","Invalid, revoked or unauthorized API token.");if(r.status===429)throw new O("quota_exceeded","API call quota exceeded for the current plan.");if(!r.ok)throw new O("internal",`Usage tracking failed with HTTP ${r.status}.`);return await r.json()}async readQueue(){try{let e=await this.kv?.getItem(He);if(!e)return[];let r=JSON.parse(e);return Array.isArray(r)?r:[]}catch{return[]}}async writeQueue(e){try{e.length===0?await this.kv?.removeItem(He):await this.kv?.setItem(He,JSON.stringify(e.slice(-Ht)))}catch{}}async enqueue(e){let r=await this.readQueue();r.push(e),await this.writeQueue(r)}};var $=class extends Error{constructor(r,s){super(s);this.code=r;this.name="SkeletonFetchError"}};async function Be(t,e={}){let r=t.trim();if(!r||r==="true"||r==="false")throw new $("invalid","skeleton uuid is required");let s=(e.baseUrl??j).replace(/\/$/,""),i;try{i=await fetch(`${s}/api/sdk/skeleton?uuid=${encodeURIComponent(r)}`)}catch(u){throw new $("network",`Skeleton fetch failed: ${String(u)}`)}if(i.status===404)throw new $("not_found",`Skeleton '${r}' not found`);if(!i.ok)throw new $("network",`Skeleton fetch HTTP ${i.status}`);let n=await i.json();if(!n?.keypoints||!n?.keypoint_lines||!n?.circles||!n?.lines)throw new $("invalid","Skeleton payload incomplete");return{keypoints:n.keypoints,keypoint_lines:n.keypoint_lines,keypoint_angles:n.keypoint_angles??[],circles:n.circles,lines:n.lines,angles:n.angles}}async function Ve(t={}){if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw new Error("getUserMedia is not available in this environment");let e=t.facingMode??"user",r={audio:!1,video:{facingMode:e,width:t.idealWidth?{ideal:t.idealWidth}:void 0,height:t.idealHeight?{ideal:t.idealHeight}:void 0,frameRate:t.idealFrameRate?{ideal:t.idealFrameRate}:void 0}};try{return await navigator.mediaDevices.getUserMedia(r)}catch{return navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:e}})}}function Ke(t){if(t)for(let e of t.getTracks())try{e.stop()}catch{}}function ee(t){return typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement}function ft(t){return typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement}function $e(t){return typeof ImageBitmap<"u"&&t instanceof ImageBitmap}function te(t){return $e(t)?{width:t.width||0,height:t.height||0}:ee(t)?{width:t.videoWidth||0,height:t.videoHeight||0}:{width:t.naturalWidth||t.width||0,height:t.naturalHeight||t.height||0}}async function ve(t){if(typeof t=="string")return{url:t,revoke:()=>{}};let e=URL.createObjectURL(t);return{url:e,revoke:()=>{try{URL.revokeObjectURL(e)}catch{}}}}function We(t,e=15e3){return new Promise((r,s)=>{if(t.readyState>=2&&t.videoWidth>0){r();return}let i=()=>{f(),r()},n=()=>{f(),s(new Error("Failed to load video source"))},u=setTimeout(()=>{f(),s(new Error("Timed out waiting for video metadata"))},e),f=()=>{clearTimeout(u),t.removeEventListener("loadeddata",i),t.removeEventListener("error",n)};t.addEventListener("loadeddata",i),t.addEventListener("error",n)})}function xe(t,e=15e3){return new Promise((r,s)=>{if(t.complete&&t.naturalWidth>0){r();return}if(t.complete){s(new Error("Image failed to decode (naturalWidth=0)"));return}let i=()=>{f(),r()},n=()=>{f(),s(new Error("Failed to load image source"))},u=setTimeout(()=>{f(),s(new Error("Timed out waiting for image"))},e),f=()=>{clearTimeout(u),t.removeEventListener("load",i),t.removeEventListener("error",n)};t.addEventListener("load",i),t.addEventListener("error",n)})}async function gt(t){let e=document.createElement("canvas");e.width=t.width,e.height=t.height;let r=e.getContext("2d");if(!r)throw new Error("2d context unavailable");r.drawImage(t,0,0);let s=await new Promise((i,n)=>{e.toBlob(u=>u?i(u):n(new Error("toBlob failed")),"image/png")});return ve(s)}function we(t,e){if(t&&typeof t=="object"&&"type"in t)return t;let r=t==="video"||t==="image"||t==="camera"?t:"camera";if(r==="camera")return{type:"camera",facingMode:e?.facingMode};let s=e?.sourceFile??e?.sourceUrl;if(!s)throw new Error(`PoseSource type "${r}" requires sourceUrl or sourceFile (or pass a full PoseSource object)`);return r==="video"?{type:"video",src:s}:{type:"image",src:s}}var je={face_squat:"squat",face_pushup:"push_up",face_plank:"plank",face_lunge:"lunge",face_jumping_jack:"jumping_jack",face_low_impact_jack:"low_impact_jack",face_balance_leg:"balance_leg",face_balance_leg_left:"balance_leg_left",face_balance_leg_right:"balance_leg_right",jump:"air_time_jump"};function ze(t){return je[t]??t}function re(t,e){let r=e.find(n=>n.id===t);if(r)return r;let s=ze(t),i=Object.entries(je).filter(([,n])=>n===s).map(([n])=>n);for(let n of i){let u=e.find(f=>f.id===n);if(u)return u}return e.find(n=>ze(n.id)===s)}var yt=["shoulder_roll","shoulder_deep_breath","chair_forward_fold"];function q(t){return t==="v4"?"v4":"v3"}function Ge(t){return yt.includes(t)}var Bt={left_knee:"from_hip_to_ankle",right_knee:"from_hip_to_ankle",left_hip:"from_knee_to_shoulder",right_hip:"from_knee_to_shoulder",left_shoulder:"from_hip_to_elbow",right_shoulder:"from_hip_to_elbow",left_elbow:"from_shoulder_to_wrist",right_elbow:"from_shoulder_to_wrist"};function Vt(t){let e={left_side:{},right_side:{}};for(let r of t){if(r.side!=="left"&&r.side!=="right")continue;let s=r.side==="left"?e.left_side:e.right_side,n=`${r.id.replace(/^(left_|right_)/,"")}_angle`,u=Bt[r.id]??"value";s[n]||(s[n]={}),s[n][u]=r.degrees,s[n][`score_${u}`]=r.score}return e}function qe(t){switch(t.type){case"initialization":return{type:"initialization",message:t.step==="configuring"?"checking you plan and access":t.message??Kt(t.step),ready:t.ready,step:t.step,...t.mode!=null?{mode:t.mode}:{},...t.acceleration!=null?{acceleration:t.acceleration}:{}};case"error":return{type:"error",error:t.code,message:t.message};case"warning":return{type:"warning",error:t.code,message:t.message};case"keypoints":return{type:"keypoints",data:t.keypoints.map(e=>({name:e.name,x:e.x,y:e.y,...e.z!=null?{z:e.z}:{},score:e.score})),score:t.score,timestampMs:t.timestampMs};case"stats":return{type:"stats",fps:t.fps,medianInferenceMs:t.medianInferenceMs,backend:t.backend,keypointsAbove03:t.keypointsAbove03,meanScore:t.meanScore,videoSize:t.videoSize,timestampMs:t.timestampMs};case"angles":return{type:"angles",data:Vt(t.angles),timestampMs:t.timestampMs};case"counter":return{type:"counter",current_count:t.count,...t.formScore!=null?{form_score:{score:t.formScore.score,avg_score:t.formScore.average,grade:t.formScore.grade}}:{},...t.referenceScore!=null?{reference_score:{overallScore:t.referenceScore}}:{},timestampMs:t.timestampMs};case"posture":return{type:"posture",message:t.hint,ready:t.ready,direction:t.direction??"",requirements:{missingKeypoints:t.missingKeypoints},timestampMs:t.timestampMs};case"progression":return{type:"progression",value:t.value,timestampMs:t.timestampMs};case"recommendations":return{type:"recommendations",data:t.recommendations,timestampMs:t.timestampMs};case"form_score":return{type:"form_score",score:t.score,avg_score:t.average,grade:t.grade,timestampMs:t.timestampMs};case"exercise_summary":return{type:"exercise_summary",exercise:t.exercise,counter:t.counter,avg_rep_score:t.averageFormScore,avg_similarity:t.averageSimilarity,grade:t.grade,history:t.history,durationMs:t.durationMs,timestampMs:t.timestampMs};case"jump_calibration":return{type:"jump_calibration",calibrated:t.calibrated,cmPerPixel:t.cmPerPixel,baselineY:t.baselineY,visibleHips:t.visibleHips,message:t.message,timestampMs:t.timestampMs};case"jump_started":return{type:"jump_started",message:t.message,timestampMs:t.timestampMs};case"jump_height":{let{type:e,timestampMs:r,...s}=t;return{type:e,...s,timestampMs:r}}case"jump_discarded":return{type:"jump_discarded",reason:t.reason,userMessage:t.userMessage,timestampMs:t.timestampMs};case"jump_result":{let{type:e,timestampMs:r,...s}=t;return{type:e,...s,timestampMs:r}}case"jump_summary":{let{type:e,timestampMs:r,...s}=t;return{type:e,...s,timestampMs:r}}case"performance_warning":return{type:"error",error:t.code,message:t.message,meanFps:t.meanFps,thresholdFps:t.thresholdFps,activeProfile:t.activeProfile,medianInferenceMs:t.medianInferenceMs,videoSize:t.videoSize,timestampMs:t.timestampMs};case"quality_changed":return{type:"warning",error:"quality_downgraded",message:t.detail,previousProfile:t.previousProfile,activeProfile:t.activeProfile,reason:t.reason,timestampMs:t.timestampMs};case"runtime_download_progress":return{type:"initialization",message:`downloading pose runtime ${t.completedParts+1}/${t.totalParts}`,ready:!1,step:"downloading",part:t.part,completedParts:t.completedParts,totalParts:t.totalParts,timestampMs:t.timestampMs};default:return{type:"unknown",event:t}}}function Kt(t){switch(t){case"configuring":return"checking you plan and access";case"downloading":return"downloading engine";case"warming":return"loading pose model";case"ready":return"running";default:return t}}var N=["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"];var ke="4.22.0",$t="https://cdn.jsdelivr.net/npm";function Ye(t=$t,e=ke){let r=t.replace(/\/$/,"");return{scriptUrls:[`${r}/@tensorflow/tfjs-core@${e}/dist/tf-core.min.js`,`${r}/@tensorflow/tfjs-converter@${e}/dist/tf-converter.min.js`,`${r}/@tensorflow/tfjs-backend-webgl@${e}/dist/tf-backend-webgl.min.js`,`${r}/@tensorflow/tfjs-backend-wasm@${e}/dist/tf-backend-wasm.min.js`],wasmPath:`${r}/@tensorflow/tfjs-backend-wasm@${e}/dist/`}}var Ee=null;function Wt(t){try{t.env().set("WEBGL_CPU_FORWARD",!1)}catch{}try{t.env().set("WEBGL_PACK",!1)}catch{}try{t.env().set("WEBGL_FORCE_F16_TEXTURES",!1)}catch{}try{t.env().set("WEBGL_RENDER_FLOAT32_ENABLED",!1)}catch{}try{t.env().set("WEBGL_FLUSH_THRESHOLD",1.75)}catch{}}function zt(){return new Function("s","return import(s)")("@tensorflow/tfjs")}async function se(){if(Ee)return Ee;if(typeof window<"u"&&window.tf)return Ee=window.tf,window.tf;try{let t=await zt();return Ee=t,t}catch{}throw new Error("TensorFlow.js not found. Install peer `@tensorflow/tfjs` or load CDN scripts (see README) so `window.tf` is available.")}async function oe(t){Wt(t);try{let e=await t.setBackend("webgl");if(await t.ready(),e!==!1&&t.getBackend()==="webgl")return"webgl"}catch{}return await t.setBackend("cpu"),await t.ready(),"cpu"}var vt="2.1.3",jt="https://cdn.jsdelivr.net/npm",ie=null,ne=null;function xt(t=jt,e=vt){return`${t.replace(/\/$/,"")}/@tensorflow-models/pose-detection@${e}/dist/pose-detection.min.js`}function Gt(){return new Function("s","return import(s)")("@tensorflow-models/pose-detection")}function Xe(){if(typeof window>"u")return null;let t=window.poseDetection;return t&&typeof t.createDetector=="function"&&t.SupportedModels?.BlazePose?t:null}function qt(t){if(typeof document>"u")return Promise.reject(new Error("Cannot inject pose-detection outside a browser document"));let e=Xe();return e?Promise.resolve(e):ne||(ne=new Promise((r,s)=>{let n=document.querySelector('script[data-pt-pose-detection="1"]'),u=()=>{let l=Xe();if(l){r(l);return}s(new Error("pose-detection CDN script loaded but `window.poseDetection` is missing. Check script order (TF.js first) and network."))};if(n){if(n.dataset.ptLoaded==="1"){u();return}n.addEventListener("load",u,{once:!0}),n.addEventListener("error",()=>s(new Error(`Failed to load pose-detection from ${n.src}`)),{once:!0});return}let f=document.createElement("script");f.src=t,f.async=!0,f.dataset.ptPoseDetection="1",f.onload=()=>{f.dataset.ptLoaded="1",u()},f.onerror=()=>{ne=null,s(new Error(`Failed to load pose-detection from ${t}`))},document.head.appendChild(f)}),ne)}async function Je(t){if(ie)return ie;let e=Xe();if(e)return ie=e,e;try{let s=await Gt();return ie=s,s}catch{}if(t?.allowCdn!==!1&&typeof window<"u"&&typeof document<"u"){let s=xt(t?.cdnBase,t?.version),i=await qt(s);return ie=i,i}throw new Error('BlazePose requires optional peer `@tensorflow-models/pose-detection`. Install it, load the CDN script so `window.poseDetection` exists, or use model="movenet" / pass a TF.js modelUrl.')}function Yt(t){return typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement?{vw:t.videoWidth||0,vh:t.videoHeight||0}:typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement?{vw:t.naturalWidth||t.width||0,vh:t.naturalHeight||t.height||0}:typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement?{vw:t.width||0,vh:t.height||0}:{vw:t.width||0,vh:t.height||0}}var Xt={nose:"nose",left_eye:"left_eye",right_eye:"right_eye",left_ear:"left_ear",right_ear:"right_ear",left_shoulder:"left_shoulder",right_shoulder:"right_shoulder",left_elbow:"left_elbow",right_elbow:"right_elbow",left_wrist:"left_wrist",right_wrist:"right_wrist",left_hip:"left_hip",right_hip:"right_hip",left_knee:"left_knee",right_knee:"right_knee",left_ankle:"left_ankle",right_ankle:"right_ankle"},Jt=new Set(["left_eye_inner","left_eye_outer","right_eye_inner","right_eye_outer","mouth_left","mouth_right"]),Qt=1280;function Zt(t,e,r,s,i,n,u){if(u<=1.5&&r>2)return{x:t*i,y:e*n};let f=r/i,l=s/n;return{x:f>0?t/f:t,y:l>0?e/l:e}}var ae=class{constructor(e){this.modelId="blazepose";this.detector=null;this.backend=null;this.modelType="lite"}getBackend(){return this.backend}async load(){let e=await se();this.backend=await oe(e);let r=await Je();this.detector=await r.createDetector(r.SupportedModels.BlazePose,{runtime:"tfjs",modelType:this.modelType,enableSmoothing:!1})}async estimate(e,r){if(!this.detector)throw new Error("BlazePose adapter not loaded");let s=Yt(e);if(!(s.vw>0&&s.vh>0))return null;r.temporalSmooth===!1&&this.resetTemporal();let i=e,n=null,u=s.vw,f=s.vh;if(typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement||typeof HTMLVideoElement<"u"&&e instanceof HTMLVideoElement||typeof HTMLCanvasElement<"u"&&e instanceof HTMLCanvasElement){let a=Math.min(1,Qt/Math.max(s.vw,s.vh));u=Math.max(1,Math.round(s.vw*a)),f=Math.max(1,Math.round(s.vh*a)),n=document.createElement("canvas"),n.width=u,n.height=f;let p=n.getContext("2d");p&&(p.drawImage(e,0,0,u,f),i=n)}let l=performance.now(),x=await this.detector.estimatePoses(i,{flipHorizontal:!1,maxPoses:1}),v=performance.now()-l,_=x[0];if(!_?.keypoints?.length)return{keypoints:N.map(a=>({name:a,x:0,y:0,score:0})),score:0,inferenceMs:v,videoKeypoints:N.map(a=>({name:a,xPx:0,yPx:0,score:0})),letterbox:{offsetX:0,offsetY:0,drawW:s.vw,drawH:s.vh,vw:s.vw,vh:s.vh}};let b=s.vw||1,g=s.vh||1,S=r.displayWidth||1,w=r.displayHeight||1,T=0;for(let a of _.keypoints)T=Math.max(T,Math.abs(a.x||0),Math.abs(a.y||0));let C=new Map,F=[];for(let a of _.keypoints){let p=String(a.name||"").toLowerCase(),h=typeof a.score=="number"?a.score:0,y=Zt(a.x,a.y,u,f,b,g,T),c=Xt[p];c?C.set(c,{x:y.x,y:y.y,score:h}):Jt.has(p)&&F.push({name:p,x:y.x,y:y.y,score:h})}let k=N.map(a=>{let p=C.get(a);return{name:a,xPx:p?.x??0,yPx:p?.y??0,score:p?.score??0}}),I=Math.max(S/b,w/g),L=(S-b*I)/2,o=(w-g*I)/2,d=0,m=k.map(a=>{let p=a.xPx*I+L,h=a.yPx*I+o,y=S>0?p/S:0,c=w>0?h/w:0;return r.facingMode==="user"&&(y=1-y),d+=a.score,{name:a.name,x:Math.min(1,Math.max(0,y)),y:Math.min(1,Math.max(0,c)),score:a.score}});for(let a of F){let p=a.x*I+L,h=a.y*I+o,y=S>0?p/S:0,c=w>0?h/w:0;r.facingMode==="user"&&(y=1-y),m.push({name:a.name,x:Math.min(1,Math.max(0,y)),y:Math.min(1,Math.max(0,c)),score:a.score})}return{keypoints:m,score:d/17,inferenceMs:v,videoKeypoints:k,letterbox:{offsetX:0,offsetY:0,drawW:b,drawH:g,vw:b,vh:g}}}resetTemporal(){try{this.detector?.reset?.()}catch{}}dispose(){try{this.detector?.dispose?.()}catch{}this.detector=null}};function wt(t){return typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement?{vw:t.videoWidth||0,vh:t.videoHeight||0}:typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement?{vw:t.naturalWidth||t.width||0,vh:t.naturalHeight||t.height||0}:typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement?{vw:t.width||0,vh:t.height||0}:{vw:t.width||0,vh:t.height||0}}var A=192,Se=.45,Et=.52;function er(t,e,r){let s=t*A,i=e*A;return{x:(s-r.offsetX)*(r.vw/r.drawW)+(r.originX??0),y:(i-r.offsetY)*(r.vh/r.drawH)+(r.originY??0)}}function kt(t,e,r,s,i){let n=Math.max(r/i.vw,s/i.vh),u=(r-i.vw*n)/2,f=(s-i.vh*n)/2;return{x:t*n+u,y:e*n+f}}var le=class{constructor(e){this.tf=null;this.model=null;this.backend=null;this.offscreen=null;this.offCtx=null;this.smoothed=null;this.lastVideoKps=null;this.stillPass=0;this.inferLb={offsetX:0,offsetY:0,drawW:A,drawH:A,vw:1,vh:1,originX:0,originY:0};this.letterbox={offsetX:0,offsetY:0,drawW:A,drawH:A,vw:1,vh:1};if(!e.resolved.modelUrl)throw new Error(e.resolved.unsupportedReason??"MoveNet requires a modelUrl");this.modelId=e.resolved.modelId,this.modelUrl=e.resolved.modelUrl}getBackend(){return this.backend}async load(){this.tf=await se(),this.backend=await oe(this.tf),this.model=await this.tf.loadGraphModel(this.modelUrl);let e=this.tf.zeros([1,A,A,3],"int32"),r=this.model.execute(e);(Array.isArray(r)?r[0]:r).dataSync(),e.dispose(),Array.isArray(r)?r.forEach(i=>i.dispose()):r.dispose()}poseCanvasCtx(){if(this.offscreen||(this.offscreen=document.createElement("canvas"),this.offscreen.width=A,this.offscreen.height=A,this.offCtx=this.offscreen.getContext("2d",{willReadFrequently:!0})),!this.offCtx)throw new Error("2d context unavailable");return this.offCtx}prepareInput(e,r){let{vw:s,vh:i}=wt(e),n=s||1,u=i||1,f=0,l=0,x=n,v=u;if(r&&this.lastVideoKps&&this.stillPass>0){let L=this.lastVideoKps.filter(o=>o.score>=.15);if(L.length>=4){let o=1/0,d=1/0,m=-1/0,a=-1/0;for(let P of L)o=Math.min(o,P.xPx),d=Math.min(d,P.yPx),m=Math.max(m,P.xPx),a=Math.max(a,P.yPx);let p=Math.max(8,m-o),h=Math.max(8,a-d),y=(o+m)/2,c=(d+a)/2;x=Math.max(p/Et,n*.35),v=Math.max(h/Et,u*.35);let E=n/u;x/v<E?x=v*E:v=x/E,f=y-x/2,l=c-v/2}}let _=Math.min(A/x,A/v),b=x*_,g=v*_,S=(A-b)/2,w=(A-g)/2;this.inferLb={offsetX:S,offsetY:w,drawW:b,drawH:g,vw:x,vh:v,originX:f,originY:l},this.letterbox={offsetX:0,offsetY:0,drawW:n,drawH:u,vw:n,vh:u};let T=this.poseCanvasCtx();T.fillStyle="#000",T.fillRect(0,0,A,A);let C=Math.max(0,f),F=Math.max(0,l),k=Math.min(n,f+x),I=Math.min(u,l+v);if(k>C&&I>F){let L=S+(C-f)*(b/x),o=w+(F-l)*(g/v),d=(k-C)*(b/x),m=(I-F)*(g/v);T.drawImage(e,C,F,k-C,I-F,L,o,d,m)}return this.stillPass+=1,this.offscreen}smooth(e){if(!this.smoothed)return this.smoothed=e.map(r=>({...r})),this.smoothed;for(let r=0;r<e.length;r++)e[r].score<.1||(this.smoothed[r].xPx=Se*e[r].xPx+(1-Se)*this.smoothed[r].xPx,this.smoothed[r].yPx=Se*e[r].yPx+(1-Se)*this.smoothed[r].yPx,this.smoothed[r].score=e[r].score);return this.smoothed}async estimate(e,r){if(!this.tf||!this.model)throw new Error("MoveNet adapter not loaded");let s=wt(e);if(!(s.vw>0&&s.vh>0))return null;let i=performance.now(),n=typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement,u=r.temporalSmooth!==!1&&n,f=this.prepareInput(e,u),l=this.tf.tidy(()=>this.tf.expandDims(this.tf.browser.fromPixels(f),0)),x=this.model.execute(l),_=(Array.isArray(x)?x[0]:x).dataSync();l.dispose(),Array.isArray(x)?x.forEach(o=>o.dispose()):x.dispose();let b=performance.now()-i,g=this.inferLb,S=this.letterbox,w=r.displayWidth||1,T=r.displayHeight||1,C=[],F=0;for(let o=0;o<17;o++){let d=Math.min(1,Math.max(0,_[o*3])),m=Math.min(1,Math.max(0,_[o*3+1])),a=_[o*3+2],p=er(m,d,g);C.push({name:N[o],xPx:p.x,yPx:p.y,score:a}),F+=a}let k=r.temporalSmooth===!1?C:this.smooth(C);this.lastVideoKps=k.map(o=>({xPx:o.xPx,yPx:o.yPx,score:o.score}));let I=k.map((o,d)=>({name:N[d],xPx:o.xPx,yPx:o.yPx,score:o.score})),L=I.map(o=>{let d=kt(o.xPx,o.yPx,w,T,S),m=w>0?d.x/w:0,a=T>0?d.y/T:0;return r.facingMode==="user"&&(m=1-m),{name:o.name,x:Math.min(1,Math.max(0,m)),y:Math.min(1,Math.max(0,a)),score:o.score}});return{raw:_,keypoints:L,score:F/17,inferenceMs:b,videoKeypoints:I,letterbox:S}}resetTemporal(){this.smoothed=null,this.lastVideoKps=null,this.stillPass=0}dispose(){try{this.model?.dispose()}catch{}this.model=null,this.smoothed=null,this.lastVideoKps=null,this.stillPass=0,this.offscreen=null,this.offCtx=null}};function Qe(t,e,r,s){return t.map(i=>{let n=kt(i.xPx,i.yPx,r,s,e);return{name:i.name,dx:n.x,dy:n.y,score:i.score}})}function Ze(t){let{resolved:e}=t;if(e.kind==="unsupported"||!e.modelUrl&&e.kind!=="blazepose")throw new Error(e.unsupportedReason??`Model "${e.modelId}" is unavailable.`);return e.kind==="blazepose"?new ae(t):new le(t)}var be="https://app.posetracker.com/scripts/tmp_model_to_remove.json";function W(t={}){let e=typeof t.modelUrl=="string"&&t.modelUrl.trim().length>0?t.modelUrl.trim():null;if(e)return{modelId:typeof t.model=="string"&&t.model.trim().length>0?t.model.trim():"custom",kind:"custom-graph",modelUrl:e};let r=(t.model??"movenet").trim().toLowerCase();return r==="movenet"||r==="movenet-singlepose-lightning"||r==="lightning"?{modelId:"movenet-singlepose-lightning",kind:"movenet-graph",modelUrl:be}:r==="blazepose"?{modelId:"blazepose",kind:"blazepose",modelUrl:null}:{modelId:r,kind:"unsupported",modelUrl:null,unsupportedReason:`Unknown model "${t.model}". Use "movenet" (default), "blazepose", or pass modelUrl.`}}var ce="AI Loading",et="https://docs.posetracker.com/logo/light.png";var tr=`
|
|
1
|
+
"use strict";var PoseTracker=(()=>{var Ft=Object.create;var me=Object.defineProperty;var Ct=Object.getOwnPropertyDescriptor;var It=Object.getOwnPropertyNames;var Lt=Object.getPrototypeOf,At=Object.prototype.hasOwnProperty;var dt=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var Rt=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),Dt=(t,e)=>{for(var r in e)me(t,r,{get:e[r],enumerable:!0})},ht=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of It(e))!At.call(t,i)&&i!==r&&me(t,i,{get:()=>e[i],enumerable:!(s=Ct(e,i))||s.enumerable});return t};var ut=(t,e,r)=>(r=t!=null?Ft(Lt(t)):{},ht(e||!t||!t.__esModule?me(r,"default",{value:t,enumerable:!0}):r,t)),Ot=t=>ht(me({},"__esModule",{value:!0}),t);var De=Rt((fr,fe)=>{"use strict";(function(){"use strict";var t="input is invalid type",e=typeof window=="object",r=e?window:{};r.JS_SHA256_NO_WINDOW&&(e=!1);var s=!e&&typeof self=="object",i=!r.JS_SHA256_NO_NODE_JS&&typeof process=="object"&&process.versions&&process.versions.node&&process.type!="renderer";i?r=global:s&&(r=self);var n=!r.JS_SHA256_NO_COMMON_JS&&typeof fe=="object"&&fe.exports,u=typeof define=="function"&&define.amd,p=!r.JS_SHA256_NO_ARRAY_BUFFER&&typeof ArrayBuffer<"u",l="0123456789abcdef".split(""),x=[-2147483648,8388608,32768,128],v=[24,16,8,0],_=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],w=["hex","array","digest","arrayBuffer"],g=[];(r.JS_SHA256_NO_NODE_JS||!Array.isArray)&&(Array.isArray=function(o){return Object.prototype.toString.call(o)==="[object Array]"}),p&&(r.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW||!ArrayBuffer.isView)&&(ArrayBuffer.isView=function(o){return typeof o=="object"&&o.buffer&&o.buffer.constructor===ArrayBuffer});var b=function(o,d){return function(f){return new k(d,!0).update(f)[o]()}},E=function(o){var d=b("hex",o);i&&(d=T(d,o)),d.create=function(){return new k(o)},d.update=function(m){return d.create().update(m)};for(var f=0;f<w.length;++f){var a=w[f];d[a]=b(a,o)}return d},T=function(o,d){var f=dt("crypto"),a=dt("buffer").Buffer,m=d?"sha224":"sha256",h;a.from&&!r.JS_SHA256_NO_BUFFER_FROM?h=a.from:h=function(c){return new a(c)};var y=function(c){if(typeof c=="string")return f.createHash(m).update(c,"utf8").digest("hex");if(c==null)throw new Error(t);return c.constructor===ArrayBuffer&&(c=new Uint8Array(c)),Array.isArray(c)||ArrayBuffer.isView(c)||c.constructor===a?f.createHash(m).update(h(c)).digest("hex"):o(c)};return y},C=function(o,d){return function(f,a){return new L(f,d,!0).update(a)[o]()}},F=function(o){var d=C("hex",o);d.create=function(m){return new L(m,o)},d.update=function(m,h){return d.create(m).update(h)};for(var f=0;f<w.length;++f){var a=w[f];d[a]=C(a,o)}return d};function k(o,d){d?(g[0]=g[16]=g[1]=g[2]=g[3]=g[4]=g[5]=g[6]=g[7]=g[8]=g[9]=g[10]=g[11]=g[12]=g[13]=g[14]=g[15]=0,this.blocks=g):this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],o?(this.h0=3238371032,this.h1=914150663,this.h2=812702999,this.h3=4144912697,this.h4=4290775857,this.h5=1750603025,this.h6=1694076839,this.h7=3204075428):(this.h0=1779033703,this.h1=3144134277,this.h2=1013904242,this.h3=2773480762,this.h4=1359893119,this.h5=2600822924,this.h6=528734635,this.h7=1541459225),this.block=this.start=this.bytes=this.hBytes=0,this.finalized=this.hashed=!1,this.first=!0,this.is224=o}k.prototype.update=function(o){if(!this.finalized){var d,f=typeof o;if(f!=="string"){if(f==="object"){if(o===null)throw new Error(t);if(p&&o.constructor===ArrayBuffer)o=new Uint8Array(o);else if(!Array.isArray(o)&&(!p||!ArrayBuffer.isView(o)))throw new Error(t)}else throw new Error(t);d=!0}for(var a,m=0,h,y=o.length,c=this.blocks;m<y;){if(this.hashed&&(this.hashed=!1,c[0]=this.block,this.block=c[16]=c[1]=c[2]=c[3]=c[4]=c[5]=c[6]=c[7]=c[8]=c[9]=c[10]=c[11]=c[12]=c[13]=c[14]=c[15]=0),d)for(h=this.start;m<y&&h<64;++m)c[h>>>2]|=o[m]<<v[h++&3];else for(h=this.start;m<y&&h<64;++m)a=o.charCodeAt(m),a<128?c[h>>>2]|=a<<v[h++&3]:a<2048?(c[h>>>2]|=(192|a>>>6)<<v[h++&3],c[h>>>2]|=(128|a&63)<<v[h++&3]):a<55296||a>=57344?(c[h>>>2]|=(224|a>>>12)<<v[h++&3],c[h>>>2]|=(128|a>>>6&63)<<v[h++&3],c[h>>>2]|=(128|a&63)<<v[h++&3]):(a=65536+((a&1023)<<10|o.charCodeAt(++m)&1023),c[h>>>2]|=(240|a>>>18)<<v[h++&3],c[h>>>2]|=(128|a>>>12&63)<<v[h++&3],c[h>>>2]|=(128|a>>>6&63)<<v[h++&3],c[h>>>2]|=(128|a&63)<<v[h++&3]);this.lastByteIndex=h,this.bytes+=h-this.start,h>=64?(this.block=c[16],this.start=h-64,this.hash(),this.hashed=!0):this.start=h}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},k.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var o=this.blocks,d=this.lastByteIndex;o[16]=this.block,o[d>>>2]|=x[d&3],this.block=o[16],d>=56&&(this.hashed||this.hash(),o[0]=this.block,o[16]=o[1]=o[2]=o[3]=o[4]=o[5]=o[6]=o[7]=o[8]=o[9]=o[10]=o[11]=o[12]=o[13]=o[14]=o[15]=0),o[14]=this.hBytes<<3|this.bytes>>>29,o[15]=this.bytes<<3,this.hash()}},k.prototype.hash=function(){var o=this.h0,d=this.h1,f=this.h2,a=this.h3,m=this.h4,h=this.h5,y=this.h6,c=this.h7,S=this.blocks,P,R,D,H,M,B,V,Y,Ae,Re,pe;for(P=16;P<64;++P)M=S[P-15],R=(M>>>7|M<<25)^(M>>>18|M<<14)^M>>>3,M=S[P-2],D=(M>>>17|M<<15)^(M>>>19|M<<13)^M>>>10,S[P]=S[P-16]+R+S[P-7]+D<<0;for(pe=d&f,P=0;P<64;P+=4)this.first?(this.is224?(Y=300032,M=S[0]-1413257819,c=M-150054599<<0,a=M+24177077<<0):(Y=704751109,M=S[0]-210244248,c=M-1521486534<<0,a=M+143694565<<0),this.first=!1):(R=(o>>>2|o<<30)^(o>>>13|o<<19)^(o>>>22|o<<10),D=(m>>>6|m<<26)^(m>>>11|m<<21)^(m>>>25|m<<7),Y=o&d,H=Y^o&f^pe,V=m&h^~m&y,M=c+D+V+_[P]+S[P],B=R+H,c=a+M<<0,a=M+B<<0),R=(a>>>2|a<<30)^(a>>>13|a<<19)^(a>>>22|a<<10),D=(c>>>6|c<<26)^(c>>>11|c<<21)^(c>>>25|c<<7),Ae=a&o,H=Ae^a&d^Y,V=c&m^~c&h,M=y+D+V+_[P+1]+S[P+1],B=R+H,y=f+M<<0,f=M+B<<0,R=(f>>>2|f<<30)^(f>>>13|f<<19)^(f>>>22|f<<10),D=(y>>>6|y<<26)^(y>>>11|y<<21)^(y>>>25|y<<7),Re=f&a,H=Re^f&o^Ae,V=y&c^~y&m,M=h+D+V+_[P+2]+S[P+2],B=R+H,h=d+M<<0,d=M+B<<0,R=(d>>>2|d<<30)^(d>>>13|d<<19)^(d>>>22|d<<10),D=(h>>>6|h<<26)^(h>>>11|h<<21)^(h>>>25|h<<7),pe=d&f,H=pe^d&a^Re,V=h&y^~h&c,M=m+D+V+_[P+3]+S[P+3],B=R+H,m=o+M<<0,o=M+B<<0,this.chromeBugWorkAround=!0;this.h0=this.h0+o<<0,this.h1=this.h1+d<<0,this.h2=this.h2+f<<0,this.h3=this.h3+a<<0,this.h4=this.h4+m<<0,this.h5=this.h5+h<<0,this.h6=this.h6+y<<0,this.h7=this.h7+c<<0},k.prototype.hex=function(){this.finalize();var o=this.h0,d=this.h1,f=this.h2,a=this.h3,m=this.h4,h=this.h5,y=this.h6,c=this.h7,S=l[o>>>28&15]+l[o>>>24&15]+l[o>>>20&15]+l[o>>>16&15]+l[o>>>12&15]+l[o>>>8&15]+l[o>>>4&15]+l[o&15]+l[d>>>28&15]+l[d>>>24&15]+l[d>>>20&15]+l[d>>>16&15]+l[d>>>12&15]+l[d>>>8&15]+l[d>>>4&15]+l[d&15]+l[f>>>28&15]+l[f>>>24&15]+l[f>>>20&15]+l[f>>>16&15]+l[f>>>12&15]+l[f>>>8&15]+l[f>>>4&15]+l[f&15]+l[a>>>28&15]+l[a>>>24&15]+l[a>>>20&15]+l[a>>>16&15]+l[a>>>12&15]+l[a>>>8&15]+l[a>>>4&15]+l[a&15]+l[m>>>28&15]+l[m>>>24&15]+l[m>>>20&15]+l[m>>>16&15]+l[m>>>12&15]+l[m>>>8&15]+l[m>>>4&15]+l[m&15]+l[h>>>28&15]+l[h>>>24&15]+l[h>>>20&15]+l[h>>>16&15]+l[h>>>12&15]+l[h>>>8&15]+l[h>>>4&15]+l[h&15]+l[y>>>28&15]+l[y>>>24&15]+l[y>>>20&15]+l[y>>>16&15]+l[y>>>12&15]+l[y>>>8&15]+l[y>>>4&15]+l[y&15];return this.is224||(S+=l[c>>>28&15]+l[c>>>24&15]+l[c>>>20&15]+l[c>>>16&15]+l[c>>>12&15]+l[c>>>8&15]+l[c>>>4&15]+l[c&15]),S},k.prototype.toString=k.prototype.hex,k.prototype.digest=function(){this.finalize();var o=this.h0,d=this.h1,f=this.h2,a=this.h3,m=this.h4,h=this.h5,y=this.h6,c=this.h7,S=[o>>>24&255,o>>>16&255,o>>>8&255,o&255,d>>>24&255,d>>>16&255,d>>>8&255,d&255,f>>>24&255,f>>>16&255,f>>>8&255,f&255,a>>>24&255,a>>>16&255,a>>>8&255,a&255,m>>>24&255,m>>>16&255,m>>>8&255,m&255,h>>>24&255,h>>>16&255,h>>>8&255,h&255,y>>>24&255,y>>>16&255,y>>>8&255,y&255];return this.is224||S.push(c>>>24&255,c>>>16&255,c>>>8&255,c&255),S},k.prototype.array=k.prototype.digest,k.prototype.arrayBuffer=function(){this.finalize();var o=new ArrayBuffer(this.is224?28:32),d=new DataView(o);return d.setUint32(0,this.h0),d.setUint32(4,this.h1),d.setUint32(8,this.h2),d.setUint32(12,this.h3),d.setUint32(16,this.h4),d.setUint32(20,this.h5),d.setUint32(24,this.h6),this.is224||d.setUint32(28,this.h7),o};function L(o,d,f){var a,m=typeof o;if(m==="string"){var h=[],y=o.length,c=0,S;for(a=0;a<y;++a)S=o.charCodeAt(a),S<128?h[c++]=S:S<2048?(h[c++]=192|S>>>6,h[c++]=128|S&63):S<55296||S>=57344?(h[c++]=224|S>>>12,h[c++]=128|S>>>6&63,h[c++]=128|S&63):(S=65536+((S&1023)<<10|o.charCodeAt(++a)&1023),h[c++]=240|S>>>18,h[c++]=128|S>>>12&63,h[c++]=128|S>>>6&63,h[c++]=128|S&63);o=h}else if(m==="object"){if(o===null)throw new Error(t);if(p&&o.constructor===ArrayBuffer)o=new Uint8Array(o);else if(!Array.isArray(o)&&(!p||!ArrayBuffer.isView(o)))throw new Error(t)}else throw new Error(t);o.length>64&&(o=new k(d,!0).update(o).array());var P=[],R=[];for(a=0;a<64;++a){var D=o[a]||0;P[a]=92^D,R[a]=54^D}k.call(this,d,f),this.update(R),this.oKeyPad=P,this.inner=!0,this.sharedMemory=f}L.prototype=new k,L.prototype.finalize=function(){if(k.prototype.finalize.call(this),this.inner){this.inner=!1;var o=this.array();k.call(this,this.is224,this.sharedMemory),this.update(this.oKeyPad),this.update(o),k.prototype.finalize.call(this)}};var I=E();I.sha256=I,I.sha224=E(!0),I.sha256.hmac=F(),I.sha224.hmac=F(!0),n?fe.exports=I:(r.sha256=I.sha256,r.sha224=I.sha224,u&&define(function(){return I}))})()});var dr={};Dt(dr,{COCO_KEYPOINT_NAMES:()=>N,DEFAULT_MOVENET_LIGHTNING_URL:()=>Me,ONLINE_TFJS_VERSION:()=>be,PoseCamera:()=>ct,SDK_NAME:()=>X,SDK_VERSION:()=>K,configure:()=>G,createPoseTracker:()=>Le,defaultTfjsCdnUrls:()=>Je,resolvePoseModel:()=>z,shouldShowWatermark:()=>ue});var K="0.3.1",X="posetracker-web";var q="https://movelytics-strapi-c78a339b7070.herokuapp.com",U=class extends Error{constructor(r,s){super(s);this.code=r;this.name="ConfigureError"}};async function G(t,e={}){let r=(e.baseUrl??q).replace(/\/$/,""),s={...t?{apiToken:t}:{},sdkName:X,sdkVersion:K,targetPlatform:"web",poseModelProfile:e.poseModelProfile??"AdaptiveChoice",locale:e.locale,localVersions:e.localVersions,engineChannel:e.engine==="v3"?"v3":"v4"},i;try{i=await fetch(`${r}/api/sdk/configure`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})}catch(n){throw new U("network",`Handshake request failed: ${String(n)}`)}if(i.status===401||i.status===403)throw new U("invalid_token","Invalid, revoked or unauthorized API token.");if(i.status===429)throw new U("quota_exceeded","API call quota exceeded for the current plan.");if(!i.ok)throw new U("internal",`Handshake failed with HTTP ${i.status}.`);return await i.json()}var Be=ut(De(),1);var Ue=ut(De(),1),Oe="ptc1:";function Ne(t){return(0,Ue.sha256)(`posetracker-sdk-cache:${t}`)}function Ut(t){let e=unescape(encodeURIComponent(t)),r=new Uint8Array(e.length);for(let s=0;s<e.length;s++)r[s]=e.charCodeAt(s);return r}function Nt(t){let e="";for(let r=0;r<t.length;r++)e+=String.fromCharCode(t[r]);return decodeURIComponent(escape(e))}function Ht(t){let e="";for(let r=0;r<t.length;r++)e+=t[r].toString(16).padStart(2,"0");return e}function Bt(t){if(t.length%2!==0||/[^0-9a-f]/.test(t))return null;let e=new Uint8Array(t.length/2);for(let r=0;r<e.length;r++)e[r]=parseInt(t.slice(r*2,r*2+2),16);return e}function pt(t,e){let r=new Uint8Array(t.length),s=0,i=0;for(;i<t.length;){let n=Ue.sha256.array(`${e}:${s++}`);for(let u=0;u<n.length&&i<t.length;u++,i++)r[i]=t[i]^n[u]}return r}function ge(t,e){return Oe+Ht(pt(Ut(t),e))}function ye(t,e){if(!t.startsWith(Oe))return null;let r=Bt(t.slice(Oe.length));if(!r)return null;try{return Nt(pt(r,e))}catch{return null}}var He="posetracker.web.";function Q(){return typeof localStorage>"u"?null:{async getItem(t){try{return localStorage.getItem(He+t)}catch{return null}},async setItem(t,e){try{localStorage.setItem(He+t,e)}catch{}},async removeItem(t){try{localStorage.removeItem(He+t)}catch{}}}}function ft(){let t=Q();return t?{read:e=>t.getItem(`file.${e}`),write:(e,r)=>t.setItem(`file.${e}`,r),remove:e=>t.removeItem(`file.${e}`)}:null}function gt(){let t=new Map;return{async read(e){return t.has(e)?t.get(e):null},async write(e,r){t.set(e,r)},async remove(e){t.delete(e)}}}function ve(){return ft()??gt()}var mt="posetracker.engine.guard.",J=class{constructor(e={}){this.lastError=null;this.files=e.fileStore!==void 0?e.fileStore:ve(),this.kv=e.keyValueStore!==void 0?e.keyValueStore:Q(),this.fetchFn=e.fetchFn??fetch,this.onDiagnostic=e.onDiagnostic,this.onDiagnostic?.(`[posetracker-web] EngineLoader fileStore=${this.files?"yes":"none"} kv=${this.kv?"localStorage":"none"}`)}async clearGuard(e){if(!e||!this.kv)return;let r=`${mt}${e.version}.${e.sha256}`;await this.kv.removeItem(r).catch(()=>{})}async load(e,r,s){if(this.lastError=null,!e)return this.lastError="manifest has no engine descriptor",null;let i=`${mt}${e.version}.${e.sha256}`;s?.forceRetry&&await this.kv?.removeItem(i).catch(()=>{});let n=await this.kv?.getItem(i);if(n==="probing"||n==="failed")return await this.kv?.setItem(i,"failed"),this.lastError=`engine crash-guard blocked version=${e.version} (state=${n}) \u2014 call configure() again to force retry`,this.onDiagnostic?.(`[posetracker-web] ${this.lastError}`),null;let u=`engine-${e.version}.sealed`,p=await this.files?.read(u);if(p!=null){let l=ye(p,r);if(l!==null&&(0,Be.sha256)(l)===e.sha256){let x=await this.evaluate(l,i);if(x)return this.onDiagnostic?.(`[posetracker-web] engine loaded from cache version=${e.version}`),{engine:x,source:"remote-cache"}}else this.onDiagnostic?.(`[posetracker-web] engine cache purged (unseal/integrity mismatch) key=${u}`),await this.files?.remove(u)}try{this.onDiagnostic?.(`[posetracker-web] downloading engine version=${e.version}\u2026`);let l=await this.fetchFn(e.signedUrl);if(!l.ok)throw new Error(`HTTP ${l.status}`);let x=await l.text(),v=(0,Be.sha256)(x);if(v!==e.sha256)throw new Error(`integrity check failed (got ${v.slice(0,12)}\u2026 expected ${e.sha256.slice(0,12)}\u2026, bytes=${x.length})`);let _=await this.evaluate(x,i);if(_){try{await this.files?.write(u,ge(x,r))}catch(w){this.onDiagnostic?.("[posetracker-web] engine cache write failed (non-fatal): "+(w instanceof Error?w.message:String(w)))}return this.onDiagnostic?.(`[posetracker-web] engine loaded from download version=${e.version} bytes=${x.length}`),{engine:_,source:"remote-download"}}}catch(l){this.lastError=l instanceof Error?l.message:String(l),this.onDiagnostic?.(`[posetracker-web] engine download/load failed: ${this.lastError}`)}return this.lastError||(this.lastError="engine evaluate returned null"),null}async evaluate(e,r){await this.kv?.setItem(r,"probing");try{let s={exports:{}};new Function("module","exports",e)(s,s.exports);let n=s.exports.createEngine;if(typeof n!="function")throw new Error(`engine bundle does not export createEngine() (got ${typeof n}, keys=${Object.keys(s.exports).join(",")})`);let u=n();return await this.kv?.setItem(r,"passed"),u}catch(s){return this.lastError="engine evaluate failed: "+(s instanceof Error?s.message:String(s)),this.onDiagnostic?.(`[posetracker-web] ${this.lastError}`),await this.kv?.setItem(r,"failed"),null}}};var Ve="posetracker.track.queue",Vt=100,O=class extends Error{constructor(r,s){super(s);this.code=r;this.name="TrackError"}},Z=class{constructor(e={}){this.baseUrl=(e.baseUrl??q).replace(/\/$/,""),this.kv=e.keyValueStore!==void 0?e.keyValueStore:Q(),this.fetchFn=e.fetchFn??fetch}async trackMetered(e){return this.post({event:e.event??"camera_start",apiToken:e.apiToken,platform:"web",sdkVersion:K,params:e.params})}async trackAnonymous(e={}){let r={event:e.event??"camera_start",platform:"web",sdkVersion:K,params:e.params,queuedAtMs:Date.now()};try{await this.post({...r}),this.flushQueue()}catch{await this.enqueue(r)}}async flushQueue(){let e=await this.readQueue();if(e.length===0)return;let r=[];for(let s of e)try{await this.post({...s,params:{...s.params??{},queuedAtMs:s.queuedAtMs}})}catch{r.push(s)}await this.writeQueue(r)}async post(e){let r;try{r=await this.fetchFn(`${this.baseUrl}/api/sdk/track`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})}catch(s){throw new O("network",`Usage tracking request failed: ${String(s)}`)}if(r.status===401||r.status===403)throw new O("invalid_token","Invalid, revoked or unauthorized API token.");if(r.status===429)throw new O("quota_exceeded","API call quota exceeded for the current plan.");if(!r.ok)throw new O("internal",`Usage tracking failed with HTTP ${r.status}.`);return await r.json()}async readQueue(){try{let e=await this.kv?.getItem(Ve);if(!e)return[];let r=JSON.parse(e);return Array.isArray(r)?r:[]}catch{return[]}}async writeQueue(e){try{e.length===0?await this.kv?.removeItem(Ve):await this.kv?.setItem(Ve,JSON.stringify(e.slice(-Vt)))}catch{}}async enqueue(e){let r=await this.readQueue();r.push(e),await this.writeQueue(r)}};var $=class extends Error{constructor(r,s){super(s);this.code=r;this.name="SkeletonFetchError"}};async function Ke(t,e={}){let r=t.trim();if(!r||r==="true"||r==="false")throw new $("invalid","skeleton uuid is required");let s=(e.baseUrl??q).replace(/\/$/,""),i;try{i=await fetch(`${s}/api/sdk/skeleton?uuid=${encodeURIComponent(r)}`)}catch(u){throw new $("network",`Skeleton fetch failed: ${String(u)}`)}if(i.status===404)throw new $("not_found",`Skeleton '${r}' not found`);if(!i.ok)throw new $("network",`Skeleton fetch HTTP ${i.status}`);let n=await i.json();if(!n?.keypoints||!n?.keypoint_lines||!n?.circles||!n?.lines)throw new $("invalid","Skeleton payload incomplete");return{keypoints:n.keypoints,keypoint_lines:n.keypoint_lines,keypoint_angles:n.keypoint_angles??[],circles:n.circles,lines:n.lines,angles:n.angles}}async function $e(t={}){if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw new Error("getUserMedia is not available in this environment");let e=t.facingMode??"user",r={audio:!1,video:{facingMode:e,width:t.idealWidth?{ideal:t.idealWidth}:void 0,height:t.idealHeight?{ideal:t.idealHeight}:void 0,frameRate:t.idealFrameRate?{ideal:t.idealFrameRate}:void 0}};try{return await navigator.mediaDevices.getUserMedia(r)}catch{return navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:e}})}}function We(t){if(t)for(let e of t.getTracks())try{e.stop()}catch{}}function ee(t){return typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement}function yt(t){return typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement}function ze(t){return typeof ImageBitmap<"u"&&t instanceof ImageBitmap}function te(t){return ze(t)?{width:t.width||0,height:t.height||0}:ee(t)?{width:t.videoWidth||0,height:t.videoHeight||0}:{width:t.naturalWidth||t.width||0,height:t.naturalHeight||t.height||0}}async function xe(t){if(typeof t=="string")return{url:t,revoke:()=>{}};let e=URL.createObjectURL(t);return{url:e,revoke:()=>{try{URL.revokeObjectURL(e)}catch{}}}}function je(t,e=15e3){return new Promise((r,s)=>{if(t.readyState>=2&&t.videoWidth>0){r();return}let i=()=>{p(),r()},n=()=>{p(),s(new Error("Failed to load video source"))},u=setTimeout(()=>{p(),s(new Error("Timed out waiting for video metadata"))},e),p=()=>{clearTimeout(u),t.removeEventListener("loadeddata",i),t.removeEventListener("error",n)};t.addEventListener("loadeddata",i),t.addEventListener("error",n)})}function we(t,e=15e3){return new Promise((r,s)=>{if(t.complete&&t.naturalWidth>0){r();return}if(t.complete){s(new Error("Image failed to decode (naturalWidth=0)"));return}let i=()=>{p(),r()},n=()=>{p(),s(new Error("Failed to load image source"))},u=setTimeout(()=>{p(),s(new Error("Timed out waiting for image"))},e),p=()=>{clearTimeout(u),t.removeEventListener("load",i),t.removeEventListener("error",n)};t.addEventListener("load",i),t.addEventListener("error",n)})}async function vt(t){let e=document.createElement("canvas");e.width=t.width,e.height=t.height;let r=e.getContext("2d");if(!r)throw new Error("2d context unavailable");r.drawImage(t,0,0);let s=await new Promise((i,n)=>{e.toBlob(u=>u?i(u):n(new Error("toBlob failed")),"image/png")});return xe(s)}function Ee(t,e){if(t&&typeof t=="object"&&"type"in t)return t;let r=t==="video"||t==="image"||t==="camera"?t:"camera";if(r==="camera")return{type:"camera",facingMode:e?.facingMode};let s=e?.sourceFile??e?.sourceUrl;if(!s)throw new Error(`PoseSource type "${r}" requires sourceUrl or sourceFile (or pass a full PoseSource object)`);return r==="video"?{type:"video",src:s}:{type:"image",src:s}}var qe={face_squat:"squat",face_pushup:"push_up",face_plank:"plank",face_lunge:"lunge",face_jumping_jack:"jumping_jack",face_low_impact_jack:"low_impact_jack",face_balance_leg:"balance_leg",face_balance_leg_left:"balance_leg_left",face_balance_leg_right:"balance_leg_right",jump:"air_time_jump"};function re(t){return qe[t]??t}function se(t,e){let r=e.find(n=>n.id===t);if(r)return r;let s=re(t),i=Object.entries(qe).filter(([,n])=>n===s).map(([n])=>n);for(let n of i){let u=e.find(p=>p.id===n);if(u)return u}return e.find(n=>re(n.id)===s)}var xt=["shoulder_roll","shoulder_deep_breath","chair_forward_fold","chair_side_stretch"];function Se(t){let e=String(t||"").trim().toLowerCase();return e==="squat"||e==="face_squat"}function W(t){return t==="v3"?"v3":"v4"}function Ge(t={}){if(t.userExercise)return"v4";let e=String(t.engine||"").trim().toLowerCase();return Se(t.exercise)&&e!=="v4"?"v3":W(t.engine)}function Ye(t){return xt.includes(t)}var Kt={left_knee:"from_hip_to_ankle",right_knee:"from_hip_to_ankle",left_hip:"from_knee_to_shoulder",right_hip:"from_knee_to_shoulder",left_shoulder:"from_hip_to_elbow",right_shoulder:"from_hip_to_elbow",left_elbow:"from_shoulder_to_wrist",right_elbow:"from_shoulder_to_wrist"};function $t(t){let e={left_side:{},right_side:{}};for(let r of t){if(r.side!=="left"&&r.side!=="right")continue;let s=r.side==="left"?e.left_side:e.right_side,n=`${r.id.replace(/^(left_|right_)/,"")}_angle`,u=Kt[r.id]??"value";s[n]||(s[n]={}),s[n][u]=r.degrees,s[n][`score_${u}`]=r.score}return e}function Xe(t){switch(t.type){case"initialization":return{type:"initialization",message:t.step==="configuring"?"checking you plan and access":t.message??Wt(t.step),ready:t.ready,step:t.step,...t.mode!=null?{mode:t.mode}:{},...t.acceleration!=null?{acceleration:t.acceleration}:{}};case"error":return{type:"error",error:t.code,message:t.message};case"warning":return{type:"warning",error:t.code,message:t.message};case"keypoints":return{type:"keypoints",data:t.keypoints.map(e=>({name:e.name,x:e.x,y:e.y,...e.z!=null?{z:e.z}:{},score:e.score})),score:t.score,timestampMs:t.timestampMs};case"stats":return{type:"stats",fps:t.fps,medianInferenceMs:t.medianInferenceMs,backend:t.backend,keypointsAbove03:t.keypointsAbove03,meanScore:t.meanScore,videoSize:t.videoSize,timestampMs:t.timestampMs};case"angles":return{type:"angles",data:$t(t.angles),timestampMs:t.timestampMs};case"counter":return{type:"counter",current_count:t.count,...t.formScore!=null?{form_score:{score:t.formScore.score,avg_score:t.formScore.average,grade:t.formScore.grade}}:{},...t.referenceScore!=null?{reference_score:{overallScore:t.referenceScore}}:{},timestampMs:t.timestampMs};case"posture":return{type:"posture",message:t.hint,ready:t.ready,direction:t.direction??"",requirements:{missingKeypoints:t.missingKeypoints},timestampMs:t.timestampMs};case"progression":return{type:"progression",value:t.value,timestampMs:t.timestampMs};case"recommendations":return{type:"recommendations",data:t.recommendations,timestampMs:t.timestampMs};case"form_score":return{type:"form_score",score:t.score,avg_score:t.average,grade:t.grade,timestampMs:t.timestampMs};case"exercise_summary":return{type:"exercise_summary",exercise:t.exercise,counter:t.counter,avg_rep_score:t.averageFormScore,avg_similarity:t.averageSimilarity,grade:t.grade,history:t.history,durationMs:t.durationMs,timestampMs:t.timestampMs};case"jump_calibration":return{type:"jump_calibration",calibrated:t.calibrated,cmPerPixel:t.cmPerPixel,baselineY:t.baselineY,visibleHips:t.visibleHips,message:t.message,timestampMs:t.timestampMs};case"jump_started":return{type:"jump_started",message:t.message,timestampMs:t.timestampMs};case"jump_height":{let{type:e,timestampMs:r,...s}=t;return{type:e,...s,timestampMs:r}}case"jump_discarded":return{type:"jump_discarded",reason:t.reason,userMessage:t.userMessage,timestampMs:t.timestampMs};case"jump_result":{let{type:e,timestampMs:r,...s}=t;return{type:e,...s,timestampMs:r}}case"jump_summary":{let{type:e,timestampMs:r,...s}=t;return{type:e,...s,timestampMs:r}}case"performance_warning":return{type:"error",error:t.code,message:t.message,meanFps:t.meanFps,thresholdFps:t.thresholdFps,activeProfile:t.activeProfile,medianInferenceMs:t.medianInferenceMs,videoSize:t.videoSize,timestampMs:t.timestampMs};case"quality_changed":return{type:"warning",error:"quality_downgraded",message:t.detail,previousProfile:t.previousProfile,activeProfile:t.activeProfile,reason:t.reason,timestampMs:t.timestampMs};case"runtime_download_progress":return{type:"initialization",message:`downloading pose runtime ${t.completedParts+1}/${t.totalParts}`,ready:!1,step:"downloading",part:t.part,completedParts:t.completedParts,totalParts:t.totalParts,timestampMs:t.timestampMs};default:return{type:"unknown",event:t}}}function Wt(t){switch(t){case"configuring":return"checking you plan and access";case"downloading":return"downloading engine";case"warming":return"loading pose model";case"ready":return"running";default:return t}}var N=["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"];var be="4.22.0",zt="https://cdn.jsdelivr.net/npm";function Je(t=zt,e=be){let r=t.replace(/\/$/,"");return{scriptUrls:[`${r}/@tensorflow/tfjs-core@${e}/dist/tf-core.min.js`,`${r}/@tensorflow/tfjs-converter@${e}/dist/tf-converter.min.js`,`${r}/@tensorflow/tfjs-backend-webgl@${e}/dist/tf-backend-webgl.min.js`,`${r}/@tensorflow/tfjs-backend-wasm@${e}/dist/tf-backend-wasm.min.js`],wasmPath:`${r}/@tensorflow/tfjs-backend-wasm@${e}/dist/`}}var ke=null;function jt(t){try{t.env().set("WEBGL_CPU_FORWARD",!1)}catch{}try{t.env().set("WEBGL_PACK",!1)}catch{}try{t.env().set("WEBGL_FORCE_F16_TEXTURES",!1)}catch{}try{t.env().set("WEBGL_RENDER_FLOAT32_ENABLED",!1)}catch{}try{t.env().set("WEBGL_FLUSH_THRESHOLD",1.75)}catch{}}function qt(){return new Function("s","return import(s)")("@tensorflow/tfjs")}async function oe(){if(ke)return ke;if(typeof window<"u"&&window.tf)return ke=window.tf,window.tf;try{let t=await qt();return ke=t,t}catch{}throw new Error("TensorFlow.js not found. Install peer `@tensorflow/tfjs` or load CDN scripts (see README) so `window.tf` is available.")}async function ie(t){jt(t);try{let e=await t.setBackend("webgl");if(await t.ready(),e!==!1&&t.getBackend()==="webgl")return"webgl"}catch{}return await t.setBackend("cpu"),await t.ready(),"cpu"}var wt="2.1.3",Gt="https://cdn.jsdelivr.net/npm",ne=null,ae=null;function Et(t=Gt,e=wt){return`${t.replace(/\/$/,"")}/@tensorflow-models/pose-detection@${e}/dist/pose-detection.min.js`}function Yt(){return new Function("s","return import(s)")("@tensorflow-models/pose-detection")}function Qe(){if(typeof window>"u")return null;let t=window.poseDetection;return t&&typeof t.createDetector=="function"&&t.SupportedModels?.BlazePose?t:null}function Xt(t){if(typeof document>"u")return Promise.reject(new Error("Cannot inject pose-detection outside a browser document"));let e=Qe();return e?Promise.resolve(e):ae||(ae=new Promise((r,s)=>{let n=document.querySelector('script[data-pt-pose-detection="1"]'),u=()=>{let l=Qe();if(l){r(l);return}s(new Error("pose-detection CDN script loaded but `window.poseDetection` is missing. Check script order (TF.js first) and network."))};if(n){if(n.dataset.ptLoaded==="1"){u();return}n.addEventListener("load",u,{once:!0}),n.addEventListener("error",()=>s(new Error(`Failed to load pose-detection from ${n.src}`)),{once:!0});return}let p=document.createElement("script");p.src=t,p.async=!0,p.dataset.ptPoseDetection="1",p.onload=()=>{p.dataset.ptLoaded="1",u()},p.onerror=()=>{ae=null,s(new Error(`Failed to load pose-detection from ${t}`))},document.head.appendChild(p)}),ae)}async function Ze(t){if(ne)return ne;let e=Qe();if(e)return ne=e,e;try{let s=await Yt();return ne=s,s}catch{}if(t?.allowCdn!==!1&&typeof window<"u"&&typeof document<"u"){let s=Et(t?.cdnBase,t?.version),i=await Xt(s);return ne=i,i}throw new Error('BlazePose requires optional peer `@tensorflow-models/pose-detection`. Install it, load the CDN script so `window.poseDetection` exists, or use model="movenet" / pass a TF.js modelUrl.')}function Jt(t){return typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement?{vw:t.videoWidth||0,vh:t.videoHeight||0}:typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement?{vw:t.naturalWidth||t.width||0,vh:t.naturalHeight||t.height||0}:typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement?{vw:t.width||0,vh:t.height||0}:{vw:t.width||0,vh:t.height||0}}var Qt={nose:"nose",left_eye:"left_eye",right_eye:"right_eye",left_ear:"left_ear",right_ear:"right_ear",left_shoulder:"left_shoulder",right_shoulder:"right_shoulder",left_elbow:"left_elbow",right_elbow:"right_elbow",left_wrist:"left_wrist",right_wrist:"right_wrist",left_hip:"left_hip",right_hip:"right_hip",left_knee:"left_knee",right_knee:"right_knee",left_ankle:"left_ankle",right_ankle:"right_ankle"},Zt=new Set(["left_eye_inner","left_eye_outer","right_eye_inner","right_eye_outer","mouth_left","mouth_right"]),er=1280;function tr(t,e,r,s,i,n,u){if(u<=1.5&&r>2)return{x:t*i,y:e*n};let p=r/i,l=s/n;return{x:p>0?t/p:t,y:l>0?e/l:e}}var le=class{constructor(e){this.modelId="blazepose";this.detector=null;this.backend=null;this.modelType="lite"}getBackend(){return this.backend}async load(){let e=await oe();this.backend=await ie(e);let r=await Ze();this.detector=await r.createDetector(r.SupportedModels.BlazePose,{runtime:"tfjs",modelType:this.modelType,enableSmoothing:!1})}async estimate(e,r){if(!this.detector)throw new Error("BlazePose adapter not loaded");let s=Jt(e);if(!(s.vw>0&&s.vh>0))return null;r.temporalSmooth===!1&&this.resetTemporal();let i=e,n=null,u=s.vw,p=s.vh;if(typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement||typeof HTMLVideoElement<"u"&&e instanceof HTMLVideoElement||typeof HTMLCanvasElement<"u"&&e instanceof HTMLCanvasElement){let a=Math.min(1,er/Math.max(s.vw,s.vh));u=Math.max(1,Math.round(s.vw*a)),p=Math.max(1,Math.round(s.vh*a)),n=document.createElement("canvas"),n.width=u,n.height=p;let m=n.getContext("2d");m&&(m.drawImage(e,0,0,u,p),i=n)}let l=performance.now(),x=await this.detector.estimatePoses(i,{flipHorizontal:!1,maxPoses:1}),v=performance.now()-l,_=x[0];if(!_?.keypoints?.length)return{keypoints:N.map(a=>({name:a,x:0,y:0,score:0})),score:0,inferenceMs:v,videoKeypoints:N.map(a=>({name:a,xPx:0,yPx:0,score:0})),letterbox:{offsetX:0,offsetY:0,drawW:s.vw,drawH:s.vh,vw:s.vw,vh:s.vh}};let w=s.vw||1,g=s.vh||1,b=r.displayWidth||1,E=r.displayHeight||1,T=0;for(let a of _.keypoints)T=Math.max(T,Math.abs(a.x||0),Math.abs(a.y||0));let C=new Map,F=[];for(let a of _.keypoints){let m=String(a.name||"").toLowerCase(),h=typeof a.score=="number"?a.score:0,y=tr(a.x,a.y,u,p,w,g,T),c=Qt[m];c?C.set(c,{x:y.x,y:y.y,score:h}):Zt.has(m)&&F.push({name:m,x:y.x,y:y.y,score:h})}let k=N.map(a=>{let m=C.get(a);return{name:a,xPx:m?.x??0,yPx:m?.y??0,score:m?.score??0}}),L=Math.max(b/w,E/g),I=(b-w*L)/2,o=(E-g*L)/2,d=0,f=k.map(a=>{let m=a.xPx*L+I,h=a.yPx*L+o,y=b>0?m/b:0,c=E>0?h/E:0;return r.facingMode==="user"&&(y=1-y),d+=a.score,{name:a.name,x:Math.min(1,Math.max(0,y)),y:Math.min(1,Math.max(0,c)),score:a.score}});for(let a of F){let m=a.x*L+I,h=a.y*L+o,y=b>0?m/b:0,c=E>0?h/E:0;r.facingMode==="user"&&(y=1-y),f.push({name:a.name,x:Math.min(1,Math.max(0,y)),y:Math.min(1,Math.max(0,c)),score:a.score})}return{keypoints:f,score:d/17,inferenceMs:v,videoKeypoints:k,letterbox:{offsetX:0,offsetY:0,drawW:w,drawH:g,vw:w,vh:g}}}resetTemporal(){try{this.detector?.reset?.()}catch{}}dispose(){try{this.detector?.dispose?.()}catch{}this.detector=null}};function St(t){return typeof HTMLVideoElement<"u"&&t instanceof HTMLVideoElement?{vw:t.videoWidth||0,vh:t.videoHeight||0}:typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement?{vw:t.naturalWidth||t.width||0,vh:t.naturalHeight||t.height||0}:typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement?{vw:t.width||0,vh:t.height||0}:{vw:t.width||0,vh:t.height||0}}var A=192,_e=.45,kt=.52;function rr(t,e,r){let s=t*A,i=e*A;return{x:(s-r.offsetX)*(r.vw/r.drawW)+(r.originX??0),y:(i-r.offsetY)*(r.vh/r.drawH)+(r.originY??0)}}function bt(t,e,r,s,i){let n=Math.max(r/i.vw,s/i.vh),u=(r-i.vw*n)/2,p=(s-i.vh*n)/2;return{x:t*n+u,y:e*n+p}}var ce=class{constructor(e){this.tf=null;this.model=null;this.backend=null;this.offscreen=null;this.offCtx=null;this.smoothed=null;this.lastVideoKps=null;this.stillPass=0;this.inferLb={offsetX:0,offsetY:0,drawW:A,drawH:A,vw:1,vh:1,originX:0,originY:0};this.letterbox={offsetX:0,offsetY:0,drawW:A,drawH:A,vw:1,vh:1};if(!e.resolved.modelUrl)throw new Error(e.resolved.unsupportedReason??"MoveNet requires a modelUrl");this.modelId=e.resolved.modelId,this.modelUrl=e.resolved.modelUrl}getBackend(){return this.backend}async load(){this.tf=await oe(),this.backend=await ie(this.tf),this.model=await this.tf.loadGraphModel(this.modelUrl);let e=this.tf.zeros([1,A,A,3],"int32"),r=this.model.execute(e);(Array.isArray(r)?r[0]:r).dataSync(),e.dispose(),Array.isArray(r)?r.forEach(i=>i.dispose()):r.dispose()}poseCanvasCtx(){if(this.offscreen||(this.offscreen=document.createElement("canvas"),this.offscreen.width=A,this.offscreen.height=A,this.offCtx=this.offscreen.getContext("2d",{willReadFrequently:!0})),!this.offCtx)throw new Error("2d context unavailable");return this.offCtx}prepareInput(e,r){let{vw:s,vh:i}=St(e),n=s||1,u=i||1,p=0,l=0,x=n,v=u;if(r&&this.lastVideoKps&&this.stillPass>0){let I=this.lastVideoKps.filter(o=>o.score>=.15);if(I.length>=4){let o=1/0,d=1/0,f=-1/0,a=-1/0;for(let P of I)o=Math.min(o,P.xPx),d=Math.min(d,P.yPx),f=Math.max(f,P.xPx),a=Math.max(a,P.yPx);let m=Math.max(8,f-o),h=Math.max(8,a-d),y=(o+f)/2,c=(d+a)/2;x=Math.max(m/kt,n*.35),v=Math.max(h/kt,u*.35);let S=n/u;x/v<S?x=v*S:v=x/S,p=y-x/2,l=c-v/2}}let _=Math.min(A/x,A/v),w=x*_,g=v*_,b=(A-w)/2,E=(A-g)/2;this.inferLb={offsetX:b,offsetY:E,drawW:w,drawH:g,vw:x,vh:v,originX:p,originY:l},this.letterbox={offsetX:0,offsetY:0,drawW:n,drawH:u,vw:n,vh:u};let T=this.poseCanvasCtx();T.fillStyle="#000",T.fillRect(0,0,A,A);let C=Math.max(0,p),F=Math.max(0,l),k=Math.min(n,p+x),L=Math.min(u,l+v);if(k>C&&L>F){let I=b+(C-p)*(w/x),o=E+(F-l)*(g/v),d=(k-C)*(w/x),f=(L-F)*(g/v);T.drawImage(e,C,F,k-C,L-F,I,o,d,f)}return this.stillPass+=1,this.offscreen}smooth(e){if(!this.smoothed)return this.smoothed=e.map(r=>({...r})),this.smoothed;for(let r=0;r<e.length;r++)e[r].score<.1||(this.smoothed[r].xPx=_e*e[r].xPx+(1-_e)*this.smoothed[r].xPx,this.smoothed[r].yPx=_e*e[r].yPx+(1-_e)*this.smoothed[r].yPx,this.smoothed[r].score=e[r].score);return this.smoothed}async estimate(e,r){if(!this.tf||!this.model)throw new Error("MoveNet adapter not loaded");let s=St(e);if(!(s.vw>0&&s.vh>0))return null;let i=performance.now(),n=typeof HTMLImageElement<"u"&&e instanceof HTMLImageElement,u=r.temporalSmooth!==!1&&n,p=this.prepareInput(e,u),l=this.tf.tidy(()=>this.tf.expandDims(this.tf.browser.fromPixels(p),0)),x=this.model.execute(l),_=(Array.isArray(x)?x[0]:x).dataSync();l.dispose(),Array.isArray(x)?x.forEach(o=>o.dispose()):x.dispose();let w=performance.now()-i,g=this.inferLb,b=this.letterbox,E=r.displayWidth||1,T=r.displayHeight||1,C=[],F=0;for(let o=0;o<17;o++){let d=Math.min(1,Math.max(0,_[o*3])),f=Math.min(1,Math.max(0,_[o*3+1])),a=_[o*3+2],m=rr(f,d,g);C.push({name:N[o],xPx:m.x,yPx:m.y,score:a}),F+=a}let k=r.temporalSmooth===!1?C:this.smooth(C);this.lastVideoKps=k.map(o=>({xPx:o.xPx,yPx:o.yPx,score:o.score}));let L=k.map((o,d)=>({name:N[d],xPx:o.xPx,yPx:o.yPx,score:o.score})),I=L.map(o=>{let d=bt(o.xPx,o.yPx,E,T,b),f=E>0?d.x/E:0,a=T>0?d.y/T:0;return r.facingMode==="user"&&(f=1-f),{name:o.name,x:Math.min(1,Math.max(0,f)),y:Math.min(1,Math.max(0,a)),score:o.score}});return{raw:_,keypoints:I,score:F/17,inferenceMs:w,videoKeypoints:L,letterbox:b}}resetTemporal(){this.smoothed=null,this.lastVideoKps=null,this.stillPass=0}dispose(){try{this.model?.dispose()}catch{}this.model=null,this.smoothed=null,this.lastVideoKps=null,this.stillPass=0,this.offscreen=null,this.offCtx=null}};function et(t,e,r,s){return t.map(i=>{let n=bt(i.xPx,i.yPx,r,s,e);return{name:i.name,dx:n.x,dy:n.y,score:i.score}})}function tt(t){let{resolved:e}=t;if(e.kind==="unsupported"||!e.modelUrl&&e.kind!=="blazepose")throw new Error(e.unsupportedReason??`Model "${e.modelId}" is unavailable.`);return e.kind==="blazepose"?new le(t):new ce(t)}var Me="https://app.posetracker.com/scripts/tmp_model_to_remove.json";function z(t={}){let e=typeof t.modelUrl=="string"&&t.modelUrl.trim().length>0?t.modelUrl.trim():null;if(e)return{modelId:typeof t.model=="string"&&t.model.trim().length>0?t.model.trim():"custom",kind:"custom-graph",modelUrl:e};let r=(t.model??"movenet").trim().toLowerCase();return r==="movenet"||r==="movenet-singlepose-lightning"||r==="lightning"?{modelId:"movenet-singlepose-lightning",kind:"movenet-graph",modelUrl:Me}:r==="blazepose"?{modelId:"blazepose",kind:"blazepose",modelUrl:null}:{modelId:r,kind:"unsupported",modelUrl:null,unsupportedReason:`Unknown model "${t.model}". Use "movenet" (default), "blazepose", or pass modelUrl.`}}var de="AI Loading",rt="https://docs.posetracker.com/logo/light.png";var sr=`
|
|
2
2
|
.pt-root {
|
|
3
3
|
position: relative; width: 100%; height: 100%; min-height: 240px;
|
|
4
4
|
background: #000; overflow: hidden; isolation: isolate;
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
border-radius: 4px;
|
|
64
64
|
}
|
|
65
65
|
.pt-placement.show { display: block; }
|
|
66
|
-
`,
|
|
66
|
+
`,_t=!1;function or(){if(_t||typeof document>"u")return;let t=document.createElement("style");t.setAttribute("data-posetracker","shell"),t.textContent=sr,document.head.appendChild(t),_t=!0}function st(t,e={}){or();let r=e.loadingText&&e.loadingText.trim()||de,s=e.logoUrl??rt,i=document.createElement("div");i.className="pt-root",i.innerHTML=`
|
|
67
67
|
<video playsinline muted autoplay></video>
|
|
68
68
|
<img class="pt-still" alt="" draggable="false" />
|
|
69
69
|
<canvas class="pt-overlay"></canvas>
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
<img class="pt-wm-logo" alt="PoseTracker" src="${s}" />
|
|
80
80
|
</div>
|
|
81
81
|
<div class="pt-hud"></div>
|
|
82
|
-
`,t.appendChild(i);let n=i.querySelector("video"),u=i.querySelector("img.pt-still"),f=i.querySelector("canvas.pt-overlay"),l=i.querySelector(".pt-boot"),x=i.querySelector(".pt-boot-msg"),v=i.querySelector(".pt-wm"),_=i.querySelector(".pt-hud"),b=i.querySelector(".pt-placement");return{root:i,video:n,image:u,canvas:f,boot:l,bootMsg:x,watermark:v,hud:_,placement:b,setBootMessage(g,S=!1){x.textContent=g,x.classList.toggle("is-error",S)},setCameraVisible(g){g?this.setMediaMode(u.src?"image":"video"):this.setMediaMode("none")},setMediaMode(g){let S=g==="video",w=g==="image",T=S||w;n.style.opacity=S?"1":"0",u.style.opacity=w?"1":"0",f.style.opacity=T?"1":"0",l.classList.toggle("hide",T),T||(x.textContent=r,x.classList.remove("is-error"))},setWatermarkVisible(g){v.classList.toggle("show",g)},setHud(g,S){_.classList.toggle("debug",S),_.textContent=g},setPlacementVisible(g,S=10){if(b.classList.toggle("show",g),g){let w=Math.max(0,Math.min(40,S));b.style.left=`${w}%`,b.style.top=`${w}%`,b.style.right=`${w}%`,b.style.bottom=`${w}%`}},applyMirror(g){let S=g==="user"?"scaleX(-1)":"none";n.style.transform=S,u.style.transform="none",f.style.transform=S},destroy(){i.remove()}}}var rt={keypoints:["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"],keypoint_lines:["right_shoulder||left_shoulder","right_hip||left_hip","right_hip||right_shoulder","right_hip||right_knee","right_ankle||right_knee","right_shoulder||right_elbow","right_wrist||right_elbow","left_hip||left_shoulder","left_hip||left_knee","left_ankle||left_knee","left_shoulder||left_elbow","left_wrist||left_elbow"],keypoint_angles:[],angles:{fontColor:"#000000",strokeColor:"#FFC300"},circles:{circleFillColor:"#010A73",circleStrokeColor:"#FFC300",strokeWidth:"4",radius:"8"},lines:{lineStrokeColor:"#010A73",strokeWidth:"4"}};var st=.3;function ot(t,e,r,s,i=!0,n){if(t.canvas.width!==r&&(t.canvas.width=r),t.canvas.height!==s&&(t.canvas.height=s),t.clearRect(0,0,r,s),!i)return;let u=n??rt,f={};for(let w of e)f[w.name]=w;let l=u.lines.lineStrokeColor,x=Number(u.lines.strokeWidth);for(let w of u.keypoint_lines){let[T,C]=String(w).split("||"),F=f[T],k=f[C];!F||!k||F.score<=st||k.score<=st||(t.beginPath(),t.moveTo(F.dx,F.dy),t.lineTo(k.dx,k.dy),t.lineWidth=x,t.strokeStyle=l,t.stroke())}let v=u.circles.circleFillColor,_=u.circles.circleStrokeColor,b=Number(u.circles.strokeWidth),g=Number(u.circles.radius),S=new Set(u.keypoints);for(let w of e)!S.has(w.name)||w.score<=st||(t.beginPath(),t.arc(w.dx,w.dy,g,0,Math.PI*2),t.fillStyle=v,t.fill(),t.lineWidth=b,t.strokeStyle=_,t.stroke())}var bt={angles:!1,recommendations:!1,progression:!1,keypoints:!1,minGrade:null},sr=new Set(["angles","recommendations","progression","keypoints","minGrade"]),or={blazepose:'BlazePose as a features flag (use options.model = "blazepose" instead)',poseEngine:"pose engine selection (MediaPipe/PoseLandmarker)",mediapipeModel:"MediaPipe model selection",poseBackend:"pose backend selection",runInWorker:"worker thread selection"},de="You cannot use developer features. (visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)",it="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)";function _e(t){let e=or[t]??`'${t}'`;return`The '${t}' option (${e}) is not available as a features flag. Remove the option, or use options.model / modelUrl for model selection. (visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)`}function nt(t){if(!t)return{features:{...bt},unsupportedKeys:[]};let e=Object.keys(t).filter(r=>!sr.has(r));return{features:{angles:t.angles===!0,recommendations:t.recommendations===!0,progression:t.progression===!0,keypoints:t.keypoints===!0,minGrade:t.minGrade??null},unsupportedKeys:e}}function Me(t,e){let r=[];return t.angles&&r.push("angles"),t.recommendations&&r.push("recommendations"),t.progression&&r.push("progression"),t.keypoints&&e.withExercise&&r.push("keypoints"),r}function _t(t){return typeof t=="string"&&t.length>0&&t!=="free"}function he(t){return!_t(t)}var Pe="session.sealed",Te="engine.version",ir=30;function nr(t){if(typeof t=="string"){let e=document.querySelector(t);if(!e)throw new Error(`PoseTracker mount target not found: ${t}`);return e}return t}function ar(t){if(!t.length)return null;let e=[...t].sort((r,s)=>r-s);return e[Math.floor(e.length/2)]??null}function z(t){return t.facingMode?t.facingMode:t.position==="back"?"environment":"user"}var Fe=class{constructor(e,r={}){this.status="idle";this.mode="keypoints-only";this.lastError=null;this.manifest=null;this.engine=null;this.engineSource=null;this.session=null;this.currentExerciseId=null;this.listeners=new Set;this.typedListeners=new Map;this.messageListeners=new Set;this.stateListeners=new Set;this.preloadPromise=null;this.coldStartMode="basic";this.configurePromise=null;this.handshakePromise=null;this.meteredSessionState="idle";this.lastCameraStartInfo=null;this.sessionErrorStreak=0;this.featureGateReported={unsupported:!1,freeBlock:!1,missingToken:!1};this.keypointsSuppressionLogged=!1;this.shell=null;this.stream=null;this.poseSource={type:"camera"};this.objectUrlRevoke=null;this.estimateInput=null;this.imageShotPending=!1;this.adapter=null;this.running=!1;this.busy=!1;this.loopToken=0;this.cameraRevealed=!1;this.disposed=!1;this.skeletonDef=null;this.acceleration={state:"unknown",runtime:"unknown",backend:null,glRenderer:null,medianInferenceMs:null};this.inferMs=[];this.fpsWindow=0;this.fpsTimer=0;let s=e??(typeof r.apiToken=="string"?r.apiToken:null)??null;this.opts={...r,onDiagnostic:r.onDiagnostic},this.apiToken=s;let i=nt(r.features);this.features=i.features,this.unsupportedFeatureKeys=i.unsupportedKeys,this.resolved=W({model:r.model,modelUrl:r.modelUrl}),this.skeletonDef=r.skeletonDef??null,this.coldStartMode=r.coldStart==="full"?"full":"basic";try{this.poseSource=we(r.source,{sourceUrl:r.sourceUrl,sourceFile:r.sourceFile,facingMode:z(r)})}catch{this.poseSource={type:"camera",facingMode:z(r)}}this.files=r.fileStore!==void 0?r.fileStore:ye(),this.engineLoader=r.engineLoader??new J({fileStore:this.files,onDiagnostic:this.opts.onDiagnostic}),this.tracker=r.usageTracker??new Z({baseUrl:r.baseUrl}),this.opts.onDiagnostic?.(`[posetracker-web] client created hasApiToken=${!!this.apiToken} features=${JSON.stringify(this.features)}`)}getStatus(){return this.status}getMode(){return this.mode}getError(){return this.lastError}getManifest(){return this.manifest}getPlanType(){return this.manifest?.plan?.plan??null}getFeatures(){return{...this.features}}getEngineSource(){return this.engineSource}getAcceleration(){return this.acceleration.state}getAccelerationDiagnostics(){return{...this.acceleration}}getResolvedModel(){return this.resolved}getColdStartMode(){return this.coldStartMode}getSource(){return this.poseSource}addEventListener(e){return this.listeners.add(e),()=>this.listeners.delete(e)}on(e,r){if(e==="*")return this.listeners.add(r),()=>this.listeners.delete(r);let s=this.typedListeners.get(e);return s||(s=new Set,this.typedListeners.set(e,s)),s.add(r),()=>s.delete(r)}addMessageListener(e){return this.messageListeners.add(e),()=>this.messageListeners.delete(e)}onStateChange(e){return this.stateListeners.add(e),()=>this.stateListeners.delete(e)}mount(e){if(this.disposed)throw new Error("PoseTracker disposed");this.shell&&this.shell.destroy();let r=nr(e);r.innerHTML="",this.shell=tt(r,{loadingText:this.opts.loadingText??ce}),this.shell.setCameraVisible(!1),this.cameraRevealed=!1,this.resolveSkeletonFromOptions()}preload(e){let r=e?.coldStart==="full"?"full":"basic";if(this.coldStartMode=r,this.status==="ready"&&this.preloadPromise)return this.preloadPromise.then(()=>this.ensureColdStartMode(r));if(!this.preloadPromise)this.preloadPromise=this.doPreload().then(()=>this.ensureColdStartMode(r)).catch(s=>{throw this.preloadPromise=null,s});else if(r==="full")return this.preloadPromise.then(()=>this.ensureColdStartMode("full"));return this.preloadPromise}warmup(e){return this.preload(e)}async start(){if(this.disposed)throw new Error("PoseTracker disposed");if(!this.shell)throw new Error("Call mount() before start()");let e=this.poseSource.type==="camera"||this.poseSource.type==="video"||this.poseSource.type==="image";await this.preload({coldStart:this.opts.coldStart==="basic"&&this.poseSource.type==="camera"?"basic":e?"full":"basic"}),(this.coldStartMode==="basic"||!this.estimateInput)&&await this.ensureSource(),this.poseSource.type==="image"&&(this.imageShotPending=!0),this.beginLoop()}async setSource(e,r){if(this.disposed)throw new Error("PoseTracker disposed");let s=this.running;this.stopMediaOnly(),this.poseSource=we(e,{sourceUrl:r?.sourceUrl??this.opts.sourceUrl,sourceFile:r?.sourceFile??this.opts.sourceFile,facingMode:r?.facingMode??z(this.opts)}),this.opts.source=this.poseSource,this.poseSource.type==="camera"&&this.poseSource.facingMode&&(this.opts.facingMode=this.poseSource.facingMode),this.adapter?.resetTemporal&&this.adapter.resetTemporal(),this.shell&&(await this.ensureAdapter(),await this.ensureSource(),(s||this.poseSource.type==="image")&&(this.poseSource.type==="image"&&(this.imageShotPending=!0),this.beginLoop()))}async analyze(){if(this.disposed)throw new Error("PoseTracker disposed");if(!this.shell)throw new Error("Call mount() before analyze()");return await this.ensureAdapter(),this.estimateInput||await this.ensureSource(),this.poseSource.type==="image"?(this.imageShotPending=!0,this.running||this.beginLoop(),null):this.runOneFrame()}stop(){this.running=!1,this.loopToken+=1,this.busy=!1,this.imageShotPending=!1,this.stopMediaOnly(),this.shell&&(this.shell.setMediaMode("none"),this.shell.setPlacementVisible(!1),this.cameraRevealed=!1,this.applyWatermark()),this.status!=="error"&&this.setStatus("idle")}stopMediaOnly(){if(Ke(this.stream),this.stream=null,this.estimateInput=null,this.objectUrlRevoke&&(this.objectUrlRevoke(),this.objectUrlRevoke=null),this.shell){try{this.shell.video.pause()}catch{}this.shell.video.removeAttribute("src"),this.shell.video.srcObject=null,this.shell.video.load(),this.shell.image.removeAttribute("src")}}async dispose(){this.stopExercise(),this.stop(),this.adapter?.dispose(),this.adapter=null,this.shell?.destroy(),this.shell=null,this.listeners.clear(),this.typedListeners.clear(),this.messageListeners.clear(),this.stateListeners.clear(),this.preloadPromise=null,this.configurePromise=null,this.handshakePromise=null,this.meteredSessionState="idle",this.lastCameraStartInfo=null,this.featureGateReported={unsupported:!1,freeBlock:!1,missingToken:!1},this.keypointsSuppressionLogged=!1,this.engine=null,this.engineSource=null,this.manifest=null,this.mode="keypoints-only",this.setStatus("idle"),this.disposed=!0}configure(e){return e!==void 0&&e!==this.apiToken&&(this.apiToken=e,this.opts.apiToken=e,this.configurePromise=null,this.handshakePromise=null,this.featureGateReported.freeBlock=!1,this.featureGateReported.missingToken=!1),this.mode!=="full-engine"&&(this.configurePromise=null),this.configurePromise||(this.configurePromise=this.tryConfigure({silentStatus:this.status==="ready",forceEngineRetry:!0}).then(r=>(r||(this.configurePromise=null),this.retryMeteredSessionIfRefused(),r)).catch(()=>(this.configurePromise=null,!1))),this.configurePromise}getAvailableExercises(){if(this.mode!=="full-engine")return[];if(q(this.opts.engine)==="v4"){let e=this.engine?.listExercises?.()??[];if(e.length)return e.map(r=>this.v4ExerciseConfig(r))}return this.manifest?.exercises??[]}getEngineChannel(){return q(this.opts.engine)}v4ExerciseConfig(e){let r=e.type==="static"?"static":"dynamic";return{id:e.id,name:e.displayName||e.id,type:r,movement:{name:e.id,type:r,scale_acceptance:{},movement_steps:[],movement_initial_posture:null}}}getAvailableCustomExercises(){return this.mode!=="full-engine"||!this.engine?.listCustomExercises?[]:this.engine.listCustomExercises()}startExercise(e,r={}){if(this.status!=="ready")throw new Error(`Cannot start exercise while status is '${this.status}' \u2014 call preload()/start() first.`);if(this.mode!=="full-engine"||!this.engine)throw new Error("Exercises require full-engine mode (validated API key). The SDK is running keypoints-only \u2014 call configure(apiToken) first.");if(this.meteredSessionState==="refused")throw new Error("API-key features are not available offline: PoseTracker could not track this session. Reconnect and retry.");if(this.unsupportedFeatureKeys.length>0){let n=_e(this.unsupportedFeatureKeys[0]);throw this.reportError({type:"error",code:"feature_not_supported",message:n}),new Error(n)}if(this.getPlanType()==="free"&&Me(this.features,{withExercise:!0}).length>0)throw this.reportError({type:"error",code:"free_plan_feature_blocked",message:de}),new Error(de);if(Ge(e)&&q(this.opts.engine)!=="v4"){let n=`Exercise '${e}' requires engine: 'v4'`;throw this.reportError({type:"error",code:"invalid_exercise",message:n}),new Error(n)}if(q(this.opts.engine)==="v4"){let u=(this.engine.listExercises?.()??[]).find(v=>v.id===e);if(u){this.beginEngineSession(this.v4ExerciseConfig(u),r);return}let f=this.getAvailableCustomExercises(),l=f.find(v=>v.id===e)??re(e,f);if(l){this.startCustomExercise(l,r);return}let x=`Exercise '${e}' is not available in V4 engine`;throw this.reportError({type:"error",code:"invalid_exercise",message:x}),new Error(x)}let s=this.getAvailableExercises(),i=re(e,s);if(!i){let n=this.getAvailableCustomExercises(),u=n.find(l=>l.id===e)??re(e,n);if(u){this.startCustomExercise(u,r);return}let f=`Exercise '${e}' is not available in V3 engine`;throw this.reportError({type:"error",code:"invalid_exercise",message:f}),new Error(f)}this.beginEngineSession(i,r)}beginEngineSession(e,r){this.engine&&(this.stopExercise(),this.currentExerciseId=e.id,this.keypointsSuppressionLogged=!1,this.session=this.engine.createSession({exercise:e,locale:this.opts.locale??"en",difficulty:r.difficulty,minGrade:this.features.minGrade??void 0,features:{angles:this.features.angles,recommendations:this.features.recommendations,progression:this.features.progression}},s=>this.emitFromSession(s)))}startCustomExercise(e,r){if(!this.engine?.createCustomSession)throw new Error(`The cached engine bundle is too old for custom exercise '${e.id}' \u2014 reconnect so the SDK can update it.`);if(e.id==="jump_analysis"&&(!r.userHeightCm||r.userHeightCm<=0)){let s="User height (userHeightCm) must be provided for jump_analysis exercise";throw this.reportError({type:"error",code:"jump_analysis_missing_height",message:s}),new Error(s)}this.stopExercise(),this.currentExerciseId=e.id,this.keypointsSuppressionLogged=!1,this.session=this.engine.createCustomSession({exerciseId:e.id,locale:this.opts.locale??"en",userHeightCm:r.userHeightCm,devicePitchDeg:r.devicePitchDeg},s=>this.emitFromSession(s))}stopExercise(){try{this.session?.end()}catch(e){this.opts.onDiagnostic?.("[posetracker-web] engine session end() threw: "+(e instanceof Error?e.message:String(e)))}this.session=null,this.currentExerciseId=null,this.sessionErrorStreak=0,this.shell?.setPlacementVisible(!1)}getCurrentExerciseId(){return this.currentExerciseId}fetchSkeleton(e){return Be(e,{baseUrl:this.opts.baseUrl})}async setModel(e,r){let s=this.running;s&&(this.running=!1,this.loopToken+=1,this.busy=!1),this.adapter?.dispose(),this.adapter=null,e!==void 0&&(this.opts.model=e),r!==void 0&&(this.opts.modelUrl=r),this.resolved=W({model:this.opts.model,modelUrl:this.opts.modelUrl}),await this.ensureAdapter(),s&&this.beginLoop()}updateOptions(e){this.opts={...this.opts,...e},e.skeletonDef!==void 0&&(this.skeletonDef=e.skeletonDef),e.skeletonUuid&&this.resolveSkeletonFromOptions(),this.applyWatermark(),this.shell&&(e.facingMode||e.position)&&this.shell.applyMirror(z(this.opts))}ingestPose(e){if(!this.session||this.features.keypoints?this.emit({type:"keypoints",keypoints:e.keypoints,score:e.score,timestampMs:e.timestampMs}):this.keypointsSuppressionLogged||(this.keypointsSuppressionLogged=!0,this.opts.onDiagnostic?.("[posetracker-web] keypoints events paused during exercise (features.keypoints=false)")),this.session)try{this.session.processPose(e),this.sessionErrorStreak=0}catch(r){this.sessionErrorStreak+=1;let s=r instanceof Error?r.message:String(r);if(this.sessionErrorStreak===1&&this.opts.onDiagnostic?.(`[posetracker-web] processPose threw: ${s}`),this.sessionErrorStreak>=ir){let i=this.currentExerciseId;this.session=null,this.currentExerciseId=null,this.sessionErrorStreak=0,this.reportError({type:"error",code:"internal",message:`Exercise session '${i??"?"}' failed repeatedly (${s}) \u2014 session stopped.`})}}}async doPreload(){this.setStatus("configuring"),await this.resolveManifestOnce(),this.setStatus("downloading"),await this.tryConfigure({silentStatus:!0}),this.tracker.flushQueue();try{this.setStatus("warming"),await this.ensureAdapter(),this.acceleration={...this.acceleration,state:"gpu",runtime:"tfjs-webgl",backend:"webgl"}}catch(e){throw this.reportError({type:"error",code:"model_load_failed",message:e instanceof Error?e.message:String(e)}),this.setStatus("error"),e}this.setStatus("ready"),this.emit({type:"initialization",step:"ready",message:"running",ready:!0,mode:this.mode,acceleration:this.getAcceleration()})}async ensureColdStartMode(e){e==="full"&&this.shell&&await this.ensureSource()}async ensureAdapter(){if(!this.adapter){if(this.resolved=W({model:this.opts.model,modelUrl:this.opts.modelUrl}),this.resolved.kind==="unsupported")throw new Error(this.resolved.unsupportedReason??"Unsupported model");this.emit({type:"initialization",step:"loading_pose_model",message:"loading pose model",ready:!1}),this.adapter=Ze({resolved:this.resolved}),await this.adapter.load()}}async ensureSource(){if(!this.shell)throw new Error("Call mount() before opening a pose source");let e=this.poseSource;if(e.type==="camera"){if(this.stream&&this.estimateInput)return;this.emit({type:"initialization",step:"accessing_webcam",message:"accessing webcam (source=camera)",ready:!1});try{this.stream=await Ve({facingMode:e.facingMode??z(this.opts),idealWidth:this.opts.idealWidth,idealHeight:this.opts.idealHeight})}catch(r){let s=r instanceof Error?r.message:String(r),i=/NotAllowed|Permission|denied/i.test(s);throw this.reportError({type:"error",code:i?"camera_denied":"camera_unavailable",message:s}),this.shell.setBootMessage(i?"Camera permission denied \u2014 allow access in the browser":"Camera unavailable",!0),r}this.shell.video.srcObject=this.stream,this.shell.applyMirror(e.facingMode??z(this.opts)),this.shell.setMediaMode("video");try{await this.shell.video.play()}catch{}this.estimateInput=this.shell.video,this.scheduleReveal("camera"),this.handleCameraStart({backend:this.acceleration.backend??"webgl",profileId:null});return}if(e.type==="video"){this.emit({type:"initialization",step:"loading_media",message:"loading video (source=video)",ready:!1});try{await this.attachVideoSource(e.src,{autoplay:e.autoplay!==!1,loop:e.loop!==!1,muted:e.muted!==!1})}catch(r){let s=r instanceof Error?r.message:String(r);throw this.reportError({type:"error",code:"media_load_failed",message:s}),this.shell.setBootMessage("Could not load video source",!0),r}this.shell.applyMirror("none"),this.shell.setMediaMode("video"),this.scheduleReveal("video");return}this.emit({type:"initialization",step:"loading_media",message:"loading image (source=image)",ready:!1});try{await this.attachImageSource(e.src)}catch(r){let s=r instanceof Error?r.message:String(r);throw this.reportError({type:"error",code:"media_load_failed",message:s}),this.shell.setBootMessage("Could not load image source",!0),r}this.shell.applyMirror("none"),this.shell.setMediaMode("image"),this.scheduleReveal("image")}scheduleReveal(e){let r=Date.now(),s=()=>{if(this.revealCamera(),!this.cameraRevealed){if(Date.now()-r>8e3){this.shell?.setBootMessage(`${e} loaded but preview is empty \u2014 check the file or CORS`,!0);return}setTimeout(s,120)}};setTimeout(s,60)}async attachVideoSource(e,r){if(!this.shell)throw new Error("no shell");let s=this.shell.video;if(ee(e)){if(e.parentElement&&e!==s){if(this.estimateInput=e,r.autoplay)try{await e.play()}catch{}await We(e);return}e.srcObject?s.srcObject=e.srcObject:(e.currentSrc||e.src)&&(s.src=e.currentSrc||e.src)}else{let i=await ve(e);this.objectUrlRevoke=i.revoke,s.src=i.url}if(s.loop=r.loop,s.muted=r.muted,s.playsInline=!0,await We(s),r.autoplay)try{await s.play()}catch{}this.estimateInput=s}async attachImageSource(e){if(!this.shell)throw new Error("no shell");if($e(e)){let s=await gt(e);this.objectUrlRevoke=s.revoke,this.shell.image.src=s.url,await xe(this.shell.image),this.estimateInput=e;return}if(ft(e)){if(e.complete&&e.naturalWidth>0){this.shell.image.src=e.currentSrc||e.src,this.estimateInput=e;return}await xe(e),this.shell.image.src=e.currentSrc||e.src,this.estimateInput=e;return}let r=await ve(e);this.objectUrlRevoke=r.revoke,this.shell.image.src=r.url,await xe(this.shell.image),this.estimateInput=this.shell.image}beginLoop(){this.running=!0,this.fpsTimer=performance.now(),this.loop(++this.loopToken)}frameReady(e){let r=te(e);return!(!(r.width>0&&r.height>0)||ee(e)&&(e.readyState<2||this.poseSource.type==="video"&&(e.paused||e.ended)))}async runOneFrame(){if(!this.adapter||!this.shell||!this.estimateInput||!this.frameReady(this.estimateInput))return null;let e=this.estimateInput,r=this.shell.canvas.clientWidth||this.shell.root.clientWidth||1,s=this.shell.canvas.clientHeight||this.shell.root.clientHeight||1,i=te(e),n=this.poseSource.type==="image"&&i.width>0&&i.height>0,u=n?i.width:r,f=n?i.height:s,l=this.poseSource.type==="camera"?z(this.opts):"environment",x=this.poseSource.type==="image"&&this.opts.stillMode==="tracked",v=await this.adapter.estimate(e,{facingMode:l,displayWidth:u,displayHeight:f,temporalSmooth:this.poseSource.type==="camera"||this.poseSource.type==="video"||x});if(!v)return null;let _=this.shell.canvas.getContext("2d");if(_&&this.opts.drawSkeleton!==!1){let S=Qe(v.videoKeypoints,v.letterbox,r,s);ot(_,S,r,s,!0,this.skeletonDef)}else _&&this.opts.drawSkeleton===!1&&_.clearRect(0,0,r,s);let b={keypoints:v.keypoints,score:v.score,timestampMs:Date.now()};this.ingestPose(b),this.inferMs.push(v.inferenceMs),this.inferMs.length>30&&this.inferMs.shift(),this.fpsWindow+=1;let g=performance.now();if(g-this.fpsTimer>=1e3){let S=this.fpsWindow;this.fpsWindow=0,this.fpsTimer=g;let w=ar(this.inferMs);this.acceleration.medianInferenceMs=w;let T=v.keypoints.filter(k=>k.score>=.3).length,C="getBackend"in this.adapter&&typeof this.adapter.getBackend=="function"?this.adapter.getBackend():null;this.acceleration.backend=C;let F=te(e);this.opts.debugHud&&this.shell.setHud(`${this.resolved.modelId} \xB7 ${this.poseSource.type} \xB7 ${C??"?"} \xB7 ${S} fps \xB7 ${w!=null?Math.round(w)+" ms":"?"} \xB7 kp\u22650.3=${T}/17`,!0),this.emit({type:"stats",fps:S,medianInferenceMs:w,backend:C,keypointsAbove03:T,meanScore:v.score,videoSize:`${F.width}x${F.height}`,timestampMs:Date.now()})}return this.revealCamera(),b}async loop(e){if(e!==this.loopToken||!this.running||!this.shell||this.disposed)return;if(!this.adapter){requestAnimationFrame(()=>{this.loop(e)});return}let r=this.estimateInput;if(!!r&&!this.busy&&this.frameReady(r)&&(this.poseSource.type!=="image"||this.imageShotPending)){this.busy=!0;try{let n=await this.runOneFrame();if(this.poseSource.type==="image"&&n){this.imageShotPending=!1,this.running=!1,this.busy=!1;return}}catch(n){let u=n instanceof Error?n.message:String(n);if(this.emit({type:"error",code:"internal",message:`inference: ${u}`}),this.poseSource.type==="image"){this.imageShotPending=!1,this.running=!1,this.busy=!1;return}}this.busy=!1}if(e!==this.loopToken||!this.running)return;let i=r&&ee(r)?r:this.shell.video;if(this.poseSource.type!=="image"&&typeof i.requestVideoFrameCallback=="function"&&(this.poseSource.type==="video"||this.poseSource.type==="camera")){i.requestVideoFrameCallback(()=>{this.loop(e)});return}requestAnimationFrame(()=>{this.loop(e)})}async resolveSkeletonFromOptions(){if(this.opts.skeletonDef){this.skeletonDef=this.opts.skeletonDef;return}let e=this.opts.skeletonUuid?.trim();if(!e||e==="true"||e==="false"){this.skeletonDef=null;return}try{this.skeletonDef=await this.fetchSkeleton(e)}catch{this.skeletonDef=null}}resolveManifestOnce(){return this.handshakePromise||(this.handshakePromise=this.doResolveManifestOnce().then(e=>(e||(this.handshakePromise=null),e))),this.handshakePromise}async doResolveManifestOnce(){let e={poseRuntime:null,engine:await this.files?.read(Te).catch(()=>null)??null};if(this.apiToken){let r=await this.resolveManifest(this.apiToken,e);return r&&(this.manifest=r,r.revoked===!0&&await this.handleRevocation("Access revoked by the backend.")),r}try{let r=await G(null,{...this.opts,localVersions:e});return this.manifest=r,r}catch{return null}}async handleRevocation(e){let r=await this.files?.read(Te).catch(()=>null);r&&(await this.files?.remove(`engine-${r}.sealed`).catch(()=>{}),await this.files?.remove(Te).catch(()=>{})),await this.files?.remove(Pe).catch(()=>{}),this.engine=null,this.engineSource=null,this.stopExercise(),this.setMode("keypoints-only"),this.reportError({type:"error",code:"invalid_token",message:e}),this.applyWatermark()}async handleCameraStart(e){this.lastCameraStartInfo=e;let r={backend:e.backend,profileId:e.profileId,poseModelProfile:this.opts.poseModelProfile??"AdaptiveChoice",mode:this.mode,exercise:this.currentExerciseId,model:this.opts.model??"movenet",modelUrl:this.opts.modelUrl??null};if(!this.apiToken){this.tracker.trackAnonymous({event:"camera_start",params:r});return}this.meteredSessionState="pending";try{await this.tracker.trackMetered({event:"camera_start",apiToken:this.apiToken,params:r}),this.meteredSessionState="validated",this.opts.onDiagnostic?.("[posetracker-web] camera_start tracked (metered)")}catch(s){this.meteredSessionState="refused",s instanceof O&&s.code==="network"?this.reportError({type:"error",code:"offline_metered",message:"API-key features are not available offline: PoseTracker cannot count their usage. Keypoints-only keeps running."}):s instanceof O&&s.code==="invalid_token"?await this.handleRevocation("API key invalid or revoked \u2014 engine cache purged."):s instanceof O&&s.code==="quota_exceeded"?this.reportError({type:"error",code:"quota_exceeded",message:s.message}):this.reportError({type:"error",code:"internal",message:`Usage tracking failed: ${s instanceof Error?s.message:String(s)}`})}}retryMeteredSessionIfRefused(){this.meteredSessionState==="refused"&&this.apiToken&&this.lastCameraStartInfo&&this.handleCameraStart(this.lastCameraStartInfo)}async tryConfigure({silentStatus:e,forceEngineRetry:r=!1}){if(this.mode==="full-engine")return!0;if(!this.apiToken)return this.validateRequestedFeatures(),this.setMode("keypoints-only"),this.applyWatermark(),!1;let s=await this.resolveManifestOnce();if(!s||s.revoked===!0)return this.setMode("keypoints-only"),this.applyWatermark(),!1;this.manifest=s,this.validateRequestedFeatures(),e||this.setStatus("downloading");let i=Oe(this.apiToken);r&&s.engine&&await this.engineLoader.clearGuard(s.engine);let n=await this.engineLoader.load(s.engine??null,i,{forceRetry:r});if(!n){let u=this.engineLoader.lastError;return this.reportError({type:"error",code:"engine_load_failed",message:u?`Engine bundle unavailable \u2014 ${u}`:"Engine bundle unavailable \u2014 running keypoints-only."}),this.setMode("keypoints-only"),this.applyWatermark(),!1}return this.engine=n.engine,this.engineSource=n.source,s.engine?.version&&await this.files?.write(Te,s.engine.version).catch(()=>{}),this.setMode("full-engine"),this.applyWatermark(),!0}async resolveManifest(e,r){let s=Oe(e);try{let i=await G(e,{...this.opts,localVersions:r});return await this.files?.write(Pe,fe(JSON.stringify(i),s)).catch(()=>{}),i}catch(i){if(i instanceof U&&i.code==="invalid_token")return await this.handleRevocation(i.message),null;if(i instanceof U&&i.code==="quota_exceeded")return this.reportError({type:"error",code:"quota_exceeded",message:i.message}),null;let n=await this.files?.read(Pe);if(n){let u=ge(n,s);if(u)try{return JSON.parse(u)}catch{await this.files?.remove(Pe).catch(()=>{})}}return this.reportError({type:"error",code:"network",message:"Handshake unreachable and no cached session \u2014 running keypoints-only."}),null}}validateRequestedFeatures(){this.unsupportedFeatureKeys.length>0&&!this.featureGateReported.unsupported&&(this.featureGateReported.unsupported=!0,this.reportError({type:"error",code:"feature_not_supported",message:_e(this.unsupportedFeatureKeys[0])}));let e=this.features;if(e.angles||e.recommendations||e.progression||e.keypoints){if(!this.apiToken){this.featureGateReported.missingToken||(this.featureGateReported.missingToken=!0,this.reportError({type:"error",code:"invalid_token",message:it}));return}this.getPlanType()==="free"&&Me(e,{withExercise:!1}).length>0&&!this.featureGateReported.freeBlock&&(this.featureGateReported.freeBlock=!0,this.reportError({type:"error",code:"free_plan_feature_blocked",message:de}))}}emitFromSession(e){if(!(e.type==="angles"&&!this.features.angles)&&!(e.type==="recommendations"&&!this.features.recommendations)&&!(e.type==="progression"&&!this.features.progression)){if(e.type==="posture"){let r=this.opts.drawPlacementBox!==!1;this.shell?.setPlacementVisible(r&&!e.ready,this.opts.placementPaddingPercent??10)}else e.type==="exercise_summary"&&this.shell?.setPlacementVisible(!1);this.emit(e)}}watermarkOn(){return typeof this.opts.showWatermark=="boolean"?this.opts.showWatermark:this.coldStartMode==="basic"&&!this.stream?!1:he(this.getPlanType())}applyWatermark(){this.shell?.setWatermarkVisible(this.watermarkOn()&&this.cameraRevealed)}revealCamera(){if(!this.shell||this.cameraRevealed)return;let e=this.estimateInput;if(!e)return;let r=te(e);r.width>0&&r.height>0&&requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.cameraRevealed||!this.shell||(this.cameraRevealed=!0,this.poseSource.type==="image"?this.shell.setMediaMode("image"):this.shell.setMediaMode("video"),this.applyWatermark())})})}setStatus(e){this.status=e,(e==="configuring"||e==="downloading"||e==="warming")&&this.emit({type:"initialization",step:e,message:e==="configuring"?"checking you plan and access":e==="downloading"?"downloading engine":"loading pose model",ready:!1}),this.notifyState()}setMode(e){this.mode!==e&&(this.mode=e,this.notifyState())}reportError(e){this.lastError=e,this.emit(e),this.notifyState()}notifyState(){this.stateListeners.forEach(e=>{try{e()}catch{}})}emit(e){if(this.listeners.forEach(n=>{try{n(e)}catch(u){this.opts.onDiagnostic?.(`[posetracker-web] listener threw on '${e.type}': `+(u instanceof Error?u.message:String(u)))}}),this.typedListeners.get(e.type)?.forEach(n=>{try{n(e)}catch{}}),this.typedListeners.get("*")?.forEach(n=>{try{n(e)}catch{}}),this.messageListeners.size===0)return;let i=qe(e);this.messageListeners.forEach(n=>{try{n(i)}catch{}})}};function Ce(t={}){return new Fe(t.apiToken,t)}function Mt(t,e={}){let r=Ce(e);return r.mount(t),r}var at={mount:Mt};return Rt(lr);})();
|
|
82
|
+
`,t.appendChild(i);let n=i.querySelector("video"),u=i.querySelector("img.pt-still"),p=i.querySelector("canvas.pt-overlay"),l=i.querySelector(".pt-boot"),x=i.querySelector(".pt-boot-msg"),v=i.querySelector(".pt-wm"),_=i.querySelector(".pt-hud"),w=i.querySelector(".pt-placement");return{root:i,video:n,image:u,canvas:p,boot:l,bootMsg:x,watermark:v,hud:_,placement:w,setBootMessage(g,b=!1){x.textContent=g,x.classList.toggle("is-error",b)},setCameraVisible(g){g?this.setMediaMode(u.src?"image":"video"):this.setMediaMode("none")},setMediaMode(g){let b=g==="video",E=g==="image",T=b||E;n.style.opacity=b?"1":"0",u.style.opacity=E?"1":"0",p.style.opacity=T?"1":"0",l.classList.toggle("hide",T),T||(x.textContent=r,x.classList.remove("is-error"))},setWatermarkVisible(g){v.classList.toggle("show",g)},setHud(g,b){_.classList.toggle("debug",b),_.textContent=g},setPlacementVisible(g,b=10){if(w.classList.toggle("show",g),g){let E=Math.max(0,Math.min(40,b));w.style.left=`${E}%`,w.style.top=`${E}%`,w.style.right=`${E}%`,w.style.bottom=`${E}%`}},applyMirror(g){let b=g==="user"?"scaleX(-1)":"none";n.style.transform=b,u.style.transform="none",p.style.transform=b},destroy(){i.remove()}}}var ot={keypoints:["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"],keypoint_lines:["right_shoulder||left_shoulder","right_hip||left_hip","right_hip||right_shoulder","right_hip||right_knee","right_ankle||right_knee","right_shoulder||right_elbow","right_wrist||right_elbow","left_hip||left_shoulder","left_hip||left_knee","left_ankle||left_knee","left_shoulder||left_elbow","left_wrist||left_elbow"],keypoint_angles:[],angles:{fontColor:"#000000",strokeColor:"#FFC300"},circles:{circleFillColor:"#010A73",circleStrokeColor:"#FFC300",strokeWidth:"4",radius:"8"},lines:{lineStrokeColor:"#010A73",strokeWidth:"4"}};var it=.3;function nt(t,e,r,s,i=!0,n){if(t.canvas.width!==r&&(t.canvas.width=r),t.canvas.height!==s&&(t.canvas.height=s),t.clearRect(0,0,r,s),!i)return;let u=n??ot,p={};for(let E of e)p[E.name]=E;let l=u.lines.lineStrokeColor,x=Number(u.lines.strokeWidth);for(let E of u.keypoint_lines){let[T,C]=String(E).split("||"),F=p[T],k=p[C];!F||!k||F.score<=it||k.score<=it||(t.beginPath(),t.moveTo(F.dx,F.dy),t.lineTo(k.dx,k.dy),t.lineWidth=x,t.strokeStyle=l,t.stroke())}let v=u.circles.circleFillColor,_=u.circles.circleStrokeColor,w=Number(u.circles.strokeWidth),g=Number(u.circles.radius),b=new Set(u.keypoints);for(let E of e)!b.has(E.name)||E.score<=it||(t.beginPath(),t.arc(E.dx,E.dy,g,0,Math.PI*2),t.fillStyle=v,t.fill(),t.lineWidth=w,t.strokeStyle=_,t.stroke())}var Mt={angles:!1,recommendations:!1,progression:!1,keypoints:!1,minGrade:null},ir=new Set(["angles","recommendations","progression","keypoints","minGrade"]),nr={blazepose:'BlazePose as a features flag (use options.model = "blazepose" instead)',poseEngine:"pose engine selection (MediaPipe/PoseLandmarker)",mediapipeModel:"MediaPipe model selection",poseBackend:"pose backend selection",runInWorker:"worker thread selection"},he="You cannot use developer features. (visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)",at="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)";function Pe(t){let e=nr[t]??`'${t}'`;return`The '${t}' option (${e}) is not available as a features flag. Remove the option, or use options.model / modelUrl for model selection. (visit: https://posetracker.gitbook.io/posetracker-api/tracking-endpoint)`}function lt(t){if(!t)return{features:{...Mt},unsupportedKeys:[]};let e=Object.keys(t).filter(r=>!ir.has(r));return{features:{angles:t.angles===!0,recommendations:t.recommendations===!0,progression:t.progression===!0,keypoints:t.keypoints===!0,minGrade:t.minGrade??null},unsupportedKeys:e}}function Te(t,e){let r=[];return t.angles&&r.push("angles"),t.recommendations&&r.push("recommendations"),t.progression&&r.push("progression"),t.keypoints&&e.withExercise&&r.push("keypoints"),r}function Pt(t){return typeof t=="string"&&t.length>0&&t!=="free"}function ue(t){return!Pt(t)}var Fe="session.sealed",Ce="engine.version",ar=30;function lr(t){if(typeof t=="string"){let e=document.querySelector(t);if(!e)throw new Error(`PoseTracker mount target not found: ${t}`);return e}return t}function cr(t){if(!t.length)return null;let e=[...t].sort((r,s)=>r-s);return e[Math.floor(e.length/2)]??null}function j(t){return t.facingMode?t.facingMode:t.position==="back"?"environment":"user"}var Ie=class{constructor(e,r={}){this.status="idle";this.mode="keypoints-only";this.lastError=null;this.manifest=null;this.engine=null;this.engineSource=null;this.session=null;this.currentExerciseId=null;this.listeners=new Set;this.typedListeners=new Map;this.messageListeners=new Set;this.stateListeners=new Set;this.preloadPromise=null;this.coldStartMode="basic";this.configurePromise=null;this.handshakePromise=null;this.meteredSessionState="idle";this.lastCameraStartInfo=null;this.sessionErrorStreak=0;this.featureGateReported={unsupported:!1,freeBlock:!1,missingToken:!1};this.keypointsSuppressionLogged=!1;this.shell=null;this.stream=null;this.poseSource={type:"camera"};this.objectUrlRevoke=null;this.estimateInput=null;this.imageShotPending=!1;this.adapter=null;this.running=!1;this.busy=!1;this.loopToken=0;this.cameraRevealed=!1;this.disposed=!1;this.skeletonDef=null;this.acceleration={state:"unknown",runtime:"unknown",backend:null,glRenderer:null,medianInferenceMs:null};this.inferMs=[];this.fpsWindow=0;this.fpsTimer=0;let s=e??(typeof r.apiToken=="string"?r.apiToken:null)??null;this.opts={...r,onDiagnostic:r.onDiagnostic},this.apiToken=s;let i=lt(r.features);this.features=i.features,this.unsupportedFeatureKeys=i.unsupportedKeys,this.resolved=z({model:r.model,modelUrl:r.modelUrl}),this.skeletonDef=r.skeletonDef??null,this.coldStartMode=r.coldStart==="full"?"full":"basic";try{this.poseSource=Ee(r.source,{sourceUrl:r.sourceUrl,sourceFile:r.sourceFile,facingMode:j(r)})}catch{this.poseSource={type:"camera",facingMode:j(r)}}this.files=r.fileStore!==void 0?r.fileStore:ve(),this.engineLoader=r.engineLoader??new J({fileStore:this.files,onDiagnostic:this.opts.onDiagnostic}),this.tracker=r.usageTracker??new Z({baseUrl:r.baseUrl}),this.opts.onDiagnostic?.(`[posetracker-web] client created hasApiToken=${!!this.apiToken} features=${JSON.stringify(this.features)}`)}getStatus(){return this.status}getMode(){return this.mode}getError(){return this.lastError}getManifest(){return this.manifest}getPlanType(){return this.manifest?.plan?.plan??null}getFeatures(){return{...this.features}}getEngineSource(){return this.engineSource}getAcceleration(){return this.acceleration.state}getAccelerationDiagnostics(){return{...this.acceleration}}getResolvedModel(){return this.resolved}getColdStartMode(){return this.coldStartMode}getSource(){return this.poseSource}addEventListener(e){return this.listeners.add(e),()=>this.listeners.delete(e)}on(e,r){if(e==="*")return this.listeners.add(r),()=>this.listeners.delete(r);let s=this.typedListeners.get(e);return s||(s=new Set,this.typedListeners.set(e,s)),s.add(r),()=>s.delete(r)}addMessageListener(e){return this.messageListeners.add(e),()=>this.messageListeners.delete(e)}onStateChange(e){return this.stateListeners.add(e),()=>this.stateListeners.delete(e)}mount(e){if(this.disposed)throw new Error("PoseTracker disposed");this.shell&&this.shell.destroy();let r=lr(e);r.innerHTML="",this.shell=st(r,{loadingText:this.opts.loadingText??de}),this.shell.setCameraVisible(!1),this.cameraRevealed=!1,this.resolveSkeletonFromOptions()}preload(e){let r=e?.coldStart==="full"?"full":"basic";if(this.coldStartMode=r,this.status==="ready"&&this.preloadPromise)return this.preloadPromise.then(()=>this.ensureColdStartMode(r));if(!this.preloadPromise)this.preloadPromise=this.doPreload().then(()=>this.ensureColdStartMode(r)).catch(s=>{throw this.preloadPromise=null,s});else if(r==="full")return this.preloadPromise.then(()=>this.ensureColdStartMode("full"));return this.preloadPromise}warmup(e){return this.preload(e)}async start(){if(this.disposed)throw new Error("PoseTracker disposed");if(!this.shell)throw new Error("Call mount() before start()");let e=this.poseSource.type==="camera"||this.poseSource.type==="video"||this.poseSource.type==="image";await this.preload({coldStart:this.opts.coldStart==="basic"&&this.poseSource.type==="camera"?"basic":e?"full":"basic"}),(this.coldStartMode==="basic"||!this.estimateInput)&&await this.ensureSource(),this.poseSource.type==="image"&&(this.imageShotPending=!0),this.beginLoop()}async setSource(e,r){if(this.disposed)throw new Error("PoseTracker disposed");let s=this.running;this.stopMediaOnly(),this.poseSource=Ee(e,{sourceUrl:r?.sourceUrl??this.opts.sourceUrl,sourceFile:r?.sourceFile??this.opts.sourceFile,facingMode:r?.facingMode??j(this.opts)}),this.opts.source=this.poseSource,this.poseSource.type==="camera"&&this.poseSource.facingMode&&(this.opts.facingMode=this.poseSource.facingMode),this.adapter?.resetTemporal&&this.adapter.resetTemporal(),this.shell&&(await this.ensureAdapter(),await this.ensureSource(),(s||this.poseSource.type==="image")&&(this.poseSource.type==="image"&&(this.imageShotPending=!0),this.beginLoop()))}async analyze(){if(this.disposed)throw new Error("PoseTracker disposed");if(!this.shell)throw new Error("Call mount() before analyze()");return await this.ensureAdapter(),this.estimateInput||await this.ensureSource(),this.poseSource.type==="image"?(this.imageShotPending=!0,this.running||this.beginLoop(),null):this.runOneFrame()}stop(){this.running=!1,this.loopToken+=1,this.busy=!1,this.imageShotPending=!1,this.stopMediaOnly(),this.shell&&(this.shell.setMediaMode("none"),this.shell.setPlacementVisible(!1),this.cameraRevealed=!1,this.applyWatermark()),this.status!=="error"&&this.setStatus("idle")}stopMediaOnly(){if(We(this.stream),this.stream=null,this.estimateInput=null,this.objectUrlRevoke&&(this.objectUrlRevoke(),this.objectUrlRevoke=null),this.shell){try{this.shell.video.pause()}catch{}this.shell.video.removeAttribute("src"),this.shell.video.srcObject=null,this.shell.video.load(),this.shell.image.removeAttribute("src")}}async dispose(){this.stopExercise(),this.stop(),this.adapter?.dispose(),this.adapter=null,this.shell?.destroy(),this.shell=null,this.listeners.clear(),this.typedListeners.clear(),this.messageListeners.clear(),this.stateListeners.clear(),this.preloadPromise=null,this.configurePromise=null,this.handshakePromise=null,this.meteredSessionState="idle",this.lastCameraStartInfo=null,this.featureGateReported={unsupported:!1,freeBlock:!1,missingToken:!1},this.keypointsSuppressionLogged=!1,this.engine=null,this.engineSource=null,this.manifest=null,this.mode="keypoints-only",this.setStatus("idle"),this.disposed=!0}configure(e){return e!==void 0&&e!==this.apiToken&&(this.apiToken=e,this.opts.apiToken=e,this.configurePromise=null,this.handshakePromise=null,this.featureGateReported.freeBlock=!1,this.featureGateReported.missingToken=!1),this.mode!=="full-engine"&&(this.configurePromise=null),this.configurePromise||(this.configurePromise=this.tryConfigure({silentStatus:this.status==="ready",forceEngineRetry:!0}).then(r=>(r||(this.configurePromise=null),this.retryMeteredSessionIfRefused(),r)).catch(()=>(this.configurePromise=null,!1))),this.configurePromise}getAvailableExercises(){if(this.mode!=="full-engine")return[];if(W(this.opts.engine)==="v4"){let e=this.engine?.listExercises?.()??[];if(e.length)return e.map(r=>this.v4ExerciseConfig(r))}return this.manifest?.exercises??[]}getEngineChannel(){return W(this.opts.engine)}v4ExerciseConfig(e){let r=e.type==="static"?"static":"dynamic";return{id:e.id,name:e.displayName||e.id,type:r,movement:{name:e.id,type:r,scale_acceptance:{},movement_steps:[],movement_initial_posture:null}}}getAvailableCustomExercises(){return this.mode!=="full-engine"||!this.engine?.listCustomExercises?[]:this.engine.listCustomExercises()}startExercise(e,r={}){if(this.status!=="ready")throw new Error(`Cannot start exercise while status is '${this.status}' \u2014 call preload()/start() first.`);if(this.mode!=="full-engine"||!this.engine)throw new Error("Exercises require full-engine mode (validated API key). The SDK is running keypoints-only \u2014 call configure(apiToken) first.");if(this.meteredSessionState==="refused")throw new Error("API-key features are not available offline: PoseTracker could not track this session. Reconnect and retry.");if(this.unsupportedFeatureKeys.length>0){let p=Pe(this.unsupportedFeatureKeys[0]);throw this.reportError({type:"error",code:"feature_not_supported",message:p}),new Error(p)}if(this.getPlanType()==="free"&&Te(this.features,{withExercise:!0}).length>0)throw this.reportError({type:"error",code:"free_plan_feature_blocked",message:he}),new Error(he);if(Ye(e)&&W(this.opts.engine)!=="v4"){let p=`Exercise '${e}' requires engine: 'v4'`;throw this.reportError({type:"error",code:"invalid_exercise",message:p}),new Error(p)}let s=re(e)||e;if(Ge({engine:this.opts.engine,exercise:s})==="v4"){let p=this.engine.listExercises?.()??[],l=p.find(w=>w.id===e)??p.find(w=>w.id===s);if(l){this.beginEngineSession(this.v4ExerciseConfig(l),r);return}let x=this.getAvailableCustomExercises(),v=x.find(w=>w.id===e)??se(e,x);if(v){this.startCustomExercise(v,r);return}let _=`Exercise '${e}' is not available in V4 engine`;throw this.reportError({type:"error",code:"invalid_exercise",message:_}),new Error(_)}if(Se(s)&&W(this.opts.engine)==="v4"){this.beginEngineSession(this.v4ExerciseConfig({id:s||"squat",displayName:"Squat",type:"dynamic"}),r);return}let n=this.getAvailableExercises(),u=se(e,n);if(!u){let p=this.getAvailableCustomExercises(),l=p.find(v=>v.id===e)??se(e,p);if(l){this.startCustomExercise(l,r);return}let x=`Exercise '${e}' is not available in V3 engine`;throw this.reportError({type:"error",code:"invalid_exercise",message:x}),new Error(x)}this.beginEngineSession(u,r)}beginEngineSession(e,r){this.engine&&(this.stopExercise(),this.currentExerciseId=e.id,this.keypointsSuppressionLogged=!1,this.session=this.engine.createSession({exercise:e,locale:this.opts.locale??"en",difficulty:r.difficulty,minGrade:this.features.minGrade??void 0,v4CatalogSquat:this.opts.engine==="v4",features:{angles:this.features.angles,recommendations:this.features.recommendations,progression:this.features.progression}},s=>this.emitFromSession(s)))}startCustomExercise(e,r){if(!this.engine?.createCustomSession)throw new Error(`The cached engine bundle is too old for custom exercise '${e.id}' \u2014 reconnect so the SDK can update it.`);if(e.id==="jump_analysis"&&(!r.userHeightCm||r.userHeightCm<=0)){let s="User height (userHeightCm) must be provided for jump_analysis exercise";throw this.reportError({type:"error",code:"jump_analysis_missing_height",message:s}),new Error(s)}this.stopExercise(),this.currentExerciseId=e.id,this.keypointsSuppressionLogged=!1,this.session=this.engine.createCustomSession({exerciseId:e.id,locale:this.opts.locale??"en",userHeightCm:r.userHeightCm,devicePitchDeg:r.devicePitchDeg},s=>this.emitFromSession(s))}stopExercise(){try{this.session?.end()}catch(e){this.opts.onDiagnostic?.("[posetracker-web] engine session end() threw: "+(e instanceof Error?e.message:String(e)))}this.session=null,this.currentExerciseId=null,this.sessionErrorStreak=0,this.shell?.setPlacementVisible(!1)}getCurrentExerciseId(){return this.currentExerciseId}fetchSkeleton(e){return Ke(e,{baseUrl:this.opts.baseUrl})}async setModel(e,r){let s=this.running;s&&(this.running=!1,this.loopToken+=1,this.busy=!1),this.adapter?.dispose(),this.adapter=null,e!==void 0&&(this.opts.model=e),r!==void 0&&(this.opts.modelUrl=r),this.resolved=z({model:this.opts.model,modelUrl:this.opts.modelUrl}),await this.ensureAdapter(),s&&this.beginLoop()}updateOptions(e){this.opts={...this.opts,...e},e.skeletonDef!==void 0&&(this.skeletonDef=e.skeletonDef),e.skeletonUuid&&this.resolveSkeletonFromOptions(),this.applyWatermark(),this.shell&&(e.facingMode||e.position)&&this.shell.applyMirror(j(this.opts))}ingestPose(e){if(!this.session||this.features.keypoints?this.emit({type:"keypoints",keypoints:e.keypoints,score:e.score,timestampMs:e.timestampMs}):this.keypointsSuppressionLogged||(this.keypointsSuppressionLogged=!0,this.opts.onDiagnostic?.("[posetracker-web] keypoints events paused during exercise (features.keypoints=false)")),this.session)try{this.session.processPose(e),this.sessionErrorStreak=0}catch(r){this.sessionErrorStreak+=1;let s=r instanceof Error?r.message:String(r);if(this.sessionErrorStreak===1&&this.opts.onDiagnostic?.(`[posetracker-web] processPose threw: ${s}`),this.sessionErrorStreak>=ar){let i=this.currentExerciseId;this.session=null,this.currentExerciseId=null,this.sessionErrorStreak=0,this.reportError({type:"error",code:"internal",message:`Exercise session '${i??"?"}' failed repeatedly (${s}) \u2014 session stopped.`})}}}async doPreload(){this.setStatus("configuring"),await this.resolveManifestOnce(),this.setStatus("downloading"),await this.tryConfigure({silentStatus:!0}),this.tracker.flushQueue();try{this.setStatus("warming"),await this.ensureAdapter(),this.acceleration={...this.acceleration,state:"gpu",runtime:"tfjs-webgl",backend:"webgl"}}catch(e){throw this.reportError({type:"error",code:"model_load_failed",message:e instanceof Error?e.message:String(e)}),this.setStatus("error"),e}this.setStatus("ready"),this.emit({type:"initialization",step:"ready",message:"running",ready:!0,mode:this.mode,acceleration:this.getAcceleration()})}async ensureColdStartMode(e){e==="full"&&this.shell&&await this.ensureSource()}async ensureAdapter(){if(!this.adapter){if(this.resolved=z({model:this.opts.model,modelUrl:this.opts.modelUrl}),this.resolved.kind==="unsupported")throw new Error(this.resolved.unsupportedReason??"Unsupported model");this.emit({type:"initialization",step:"loading_pose_model",message:"loading pose model",ready:!1}),this.adapter=tt({resolved:this.resolved}),await this.adapter.load()}}async ensureSource(){if(!this.shell)throw new Error("Call mount() before opening a pose source");let e=this.poseSource;if(e.type==="camera"){if(this.stream&&this.estimateInput)return;this.emit({type:"initialization",step:"accessing_webcam",message:"accessing webcam (source=camera)",ready:!1});try{this.stream=await $e({facingMode:e.facingMode??j(this.opts),idealWidth:this.opts.idealWidth,idealHeight:this.opts.idealHeight})}catch(r){let s=r instanceof Error?r.message:String(r),i=/NotAllowed|Permission|denied/i.test(s);throw this.reportError({type:"error",code:i?"camera_denied":"camera_unavailable",message:s}),this.shell.setBootMessage(i?"Camera permission denied \u2014 allow access in the browser":"Camera unavailable",!0),r}this.shell.video.srcObject=this.stream,this.shell.applyMirror(e.facingMode??j(this.opts)),this.shell.setMediaMode("video");try{await this.shell.video.play()}catch{}this.estimateInput=this.shell.video,this.scheduleReveal("camera"),this.handleCameraStart({backend:this.acceleration.backend??"webgl",profileId:null});return}if(e.type==="video"){this.emit({type:"initialization",step:"loading_media",message:"loading video (source=video)",ready:!1});try{await this.attachVideoSource(e.src,{autoplay:e.autoplay!==!1,loop:e.loop!==!1,muted:e.muted!==!1})}catch(r){let s=r instanceof Error?r.message:String(r);throw this.reportError({type:"error",code:"media_load_failed",message:s}),this.shell.setBootMessage("Could not load video source",!0),r}this.shell.applyMirror("none"),this.shell.setMediaMode("video"),this.scheduleReveal("video");return}this.emit({type:"initialization",step:"loading_media",message:"loading image (source=image)",ready:!1});try{await this.attachImageSource(e.src)}catch(r){let s=r instanceof Error?r.message:String(r);throw this.reportError({type:"error",code:"media_load_failed",message:s}),this.shell.setBootMessage("Could not load image source",!0),r}this.shell.applyMirror("none"),this.shell.setMediaMode("image"),this.scheduleReveal("image")}scheduleReveal(e){let r=Date.now(),s=()=>{if(this.revealCamera(),!this.cameraRevealed){if(Date.now()-r>8e3){this.shell?.setBootMessage(`${e} loaded but preview is empty \u2014 check the file or CORS`,!0);return}setTimeout(s,120)}};setTimeout(s,60)}async attachVideoSource(e,r){if(!this.shell)throw new Error("no shell");let s=this.shell.video;if(ee(e)){if(e.parentElement&&e!==s){if(this.estimateInput=e,r.autoplay)try{await e.play()}catch{}await je(e);return}e.srcObject?s.srcObject=e.srcObject:(e.currentSrc||e.src)&&(s.src=e.currentSrc||e.src)}else{let i=await xe(e);this.objectUrlRevoke=i.revoke,s.src=i.url}if(s.loop=r.loop,s.muted=r.muted,s.playsInline=!0,await je(s),r.autoplay)try{await s.play()}catch{}this.estimateInput=s}async attachImageSource(e){if(!this.shell)throw new Error("no shell");if(ze(e)){let s=await vt(e);this.objectUrlRevoke=s.revoke,this.shell.image.src=s.url,await we(this.shell.image),this.estimateInput=e;return}if(yt(e)){if(e.complete&&e.naturalWidth>0){this.shell.image.src=e.currentSrc||e.src,this.estimateInput=e;return}await we(e),this.shell.image.src=e.currentSrc||e.src,this.estimateInput=e;return}let r=await xe(e);this.objectUrlRevoke=r.revoke,this.shell.image.src=r.url,await we(this.shell.image),this.estimateInput=this.shell.image}beginLoop(){this.running=!0,this.fpsTimer=performance.now(),this.loop(++this.loopToken)}frameReady(e){let r=te(e);return!(!(r.width>0&&r.height>0)||ee(e)&&(e.readyState<2||this.poseSource.type==="video"&&(e.paused||e.ended)))}async runOneFrame(){if(!this.adapter||!this.shell||!this.estimateInput||!this.frameReady(this.estimateInput))return null;let e=this.estimateInput,r=this.shell.canvas.clientWidth||this.shell.root.clientWidth||1,s=this.shell.canvas.clientHeight||this.shell.root.clientHeight||1,i=te(e),n=this.poseSource.type==="image"&&i.width>0&&i.height>0,u=n?i.width:r,p=n?i.height:s,l=this.poseSource.type==="camera"?j(this.opts):"environment",x=this.poseSource.type==="image"&&this.opts.stillMode==="tracked",v=await this.adapter.estimate(e,{facingMode:l,displayWidth:u,displayHeight:p,temporalSmooth:this.poseSource.type==="camera"||this.poseSource.type==="video"||x});if(!v)return null;let _=this.shell.canvas.getContext("2d");if(_&&this.opts.drawSkeleton!==!1){let b=et(v.videoKeypoints,v.letterbox,r,s);nt(_,b,r,s,!0,this.skeletonDef)}else _&&this.opts.drawSkeleton===!1&&_.clearRect(0,0,r,s);let w={keypoints:v.keypoints,score:v.score,timestampMs:Date.now()};this.ingestPose(w),this.inferMs.push(v.inferenceMs),this.inferMs.length>30&&this.inferMs.shift(),this.fpsWindow+=1;let g=performance.now();if(g-this.fpsTimer>=1e3){let b=this.fpsWindow;this.fpsWindow=0,this.fpsTimer=g;let E=cr(this.inferMs);this.acceleration.medianInferenceMs=E;let T=v.keypoints.filter(k=>k.score>=.3).length,C="getBackend"in this.adapter&&typeof this.adapter.getBackend=="function"?this.adapter.getBackend():null;this.acceleration.backend=C;let F=te(e);this.opts.debugHud&&this.shell.setHud(`${this.resolved.modelId} \xB7 ${this.poseSource.type} \xB7 ${C??"?"} \xB7 ${b} fps \xB7 ${E!=null?Math.round(E)+" ms":"?"} \xB7 kp\u22650.3=${T}/17`,!0),this.emit({type:"stats",fps:b,medianInferenceMs:E,backend:C,keypointsAbove03:T,meanScore:v.score,videoSize:`${F.width}x${F.height}`,timestampMs:Date.now()})}return this.revealCamera(),w}async loop(e){if(e!==this.loopToken||!this.running||!this.shell||this.disposed)return;if(!this.adapter){requestAnimationFrame(()=>{this.loop(e)});return}let r=this.estimateInput;if(!!r&&!this.busy&&this.frameReady(r)&&(this.poseSource.type!=="image"||this.imageShotPending)){this.busy=!0;try{let n=await this.runOneFrame();if(this.poseSource.type==="image"&&n){this.imageShotPending=!1,this.running=!1,this.busy=!1;return}}catch(n){let u=n instanceof Error?n.message:String(n);if(this.emit({type:"error",code:"internal",message:`inference: ${u}`}),this.poseSource.type==="image"){this.imageShotPending=!1,this.running=!1,this.busy=!1;return}}this.busy=!1}if(e!==this.loopToken||!this.running)return;let i=r&&ee(r)?r:this.shell.video;if(this.poseSource.type!=="image"&&typeof i.requestVideoFrameCallback=="function"&&(this.poseSource.type==="video"||this.poseSource.type==="camera")){i.requestVideoFrameCallback(()=>{this.loop(e)});return}requestAnimationFrame(()=>{this.loop(e)})}async resolveSkeletonFromOptions(){if(this.opts.skeletonDef){this.skeletonDef=this.opts.skeletonDef;return}let e=this.opts.skeletonUuid?.trim();if(!e||e==="true"||e==="false"){this.skeletonDef=null;return}try{this.skeletonDef=await this.fetchSkeleton(e)}catch{this.skeletonDef=null}}resolveManifestOnce(){return this.handshakePromise||(this.handshakePromise=this.doResolveManifestOnce().then(e=>(e||(this.handshakePromise=null),e))),this.handshakePromise}async doResolveManifestOnce(){let e={poseRuntime:null,engine:await this.files?.read(Ce).catch(()=>null)??null};if(this.apiToken){let r=await this.resolveManifest(this.apiToken,e);return r&&(this.manifest=r,r.revoked===!0&&await this.handleRevocation("Access revoked by the backend.")),r}try{let r=await G(null,{...this.opts,localVersions:e});return this.manifest=r,r}catch{return null}}async handleRevocation(e){let r=await this.files?.read(Ce).catch(()=>null);r&&(await this.files?.remove(`engine-${r}.sealed`).catch(()=>{}),await this.files?.remove(Ce).catch(()=>{})),await this.files?.remove(Fe).catch(()=>{}),this.engine=null,this.engineSource=null,this.stopExercise(),this.setMode("keypoints-only"),this.reportError({type:"error",code:"invalid_token",message:e}),this.applyWatermark()}async handleCameraStart(e){this.lastCameraStartInfo=e;let r={backend:e.backend,profileId:e.profileId,poseModelProfile:this.opts.poseModelProfile??"AdaptiveChoice",mode:this.mode,exercise:this.currentExerciseId,model:this.opts.model??"movenet",modelUrl:this.opts.modelUrl??null};if(!this.apiToken){this.tracker.trackAnonymous({event:"camera_start",params:r});return}this.meteredSessionState="pending";try{await this.tracker.trackMetered({event:"camera_start",apiToken:this.apiToken,params:r}),this.meteredSessionState="validated",this.opts.onDiagnostic?.("[posetracker-web] camera_start tracked (metered)")}catch(s){this.meteredSessionState="refused",s instanceof O&&s.code==="network"?this.reportError({type:"error",code:"offline_metered",message:"API-key features are not available offline: PoseTracker cannot count their usage. Keypoints-only keeps running."}):s instanceof O&&s.code==="invalid_token"?await this.handleRevocation("API key invalid or revoked \u2014 engine cache purged."):s instanceof O&&s.code==="quota_exceeded"?this.reportError({type:"error",code:"quota_exceeded",message:s.message}):this.reportError({type:"error",code:"internal",message:`Usage tracking failed: ${s instanceof Error?s.message:String(s)}`})}}retryMeteredSessionIfRefused(){this.meteredSessionState==="refused"&&this.apiToken&&this.lastCameraStartInfo&&this.handleCameraStart(this.lastCameraStartInfo)}async tryConfigure({silentStatus:e,forceEngineRetry:r=!1}){if(this.mode==="full-engine")return!0;if(!this.apiToken)return this.validateRequestedFeatures(),this.setMode("keypoints-only"),this.applyWatermark(),!1;let s=await this.resolveManifestOnce();if(!s||s.revoked===!0)return this.setMode("keypoints-only"),this.applyWatermark(),!1;this.manifest=s,this.validateRequestedFeatures(),e||this.setStatus("downloading");let i=Ne(this.apiToken);r&&s.engine&&await this.engineLoader.clearGuard(s.engine);let n=await this.engineLoader.load(s.engine??null,i,{forceRetry:r});if(!n){let u=this.engineLoader.lastError;return this.reportError({type:"error",code:"engine_load_failed",message:u?`Engine bundle unavailable \u2014 ${u}`:"Engine bundle unavailable \u2014 running keypoints-only."}),this.setMode("keypoints-only"),this.applyWatermark(),!1}return this.engine=n.engine,this.engineSource=n.source,s.engine?.version&&await this.files?.write(Ce,s.engine.version).catch(()=>{}),this.setMode("full-engine"),this.applyWatermark(),!0}async resolveManifest(e,r){let s=Ne(e);try{let i=await G(e,{...this.opts,localVersions:r});return await this.files?.write(Fe,ge(JSON.stringify(i),s)).catch(()=>{}),i}catch(i){if(i instanceof U&&i.code==="invalid_token")return await this.handleRevocation(i.message),null;if(i instanceof U&&i.code==="quota_exceeded")return this.reportError({type:"error",code:"quota_exceeded",message:i.message}),null;let n=await this.files?.read(Fe);if(n){let u=ye(n,s);if(u)try{return JSON.parse(u)}catch{await this.files?.remove(Fe).catch(()=>{})}}return this.reportError({type:"error",code:"network",message:"Handshake unreachable and no cached session \u2014 running keypoints-only."}),null}}validateRequestedFeatures(){this.unsupportedFeatureKeys.length>0&&!this.featureGateReported.unsupported&&(this.featureGateReported.unsupported=!0,this.reportError({type:"error",code:"feature_not_supported",message:Pe(this.unsupportedFeatureKeys[0])}));let e=this.features;if(e.angles||e.recommendations||e.progression||e.keypoints){if(!this.apiToken){this.featureGateReported.missingToken||(this.featureGateReported.missingToken=!0,this.reportError({type:"error",code:"invalid_token",message:at}));return}this.getPlanType()==="free"&&Te(e,{withExercise:!1}).length>0&&!this.featureGateReported.freeBlock&&(this.featureGateReported.freeBlock=!0,this.reportError({type:"error",code:"free_plan_feature_blocked",message:he}))}}emitFromSession(e){if(!(e.type==="angles"&&!this.features.angles)&&!(e.type==="recommendations"&&!this.features.recommendations)&&!(e.type==="progression"&&!this.features.progression)){if(e.type==="posture"){let r=this.opts.drawPlacementBox!==!1;this.shell?.setPlacementVisible(r&&!e.ready,this.opts.placementPaddingPercent??10)}else e.type==="exercise_summary"&&this.shell?.setPlacementVisible(!1);this.emit(e)}}watermarkOn(){return typeof this.opts.showWatermark=="boolean"?this.opts.showWatermark:this.coldStartMode==="basic"&&!this.stream?!1:ue(this.getPlanType())}applyWatermark(){this.shell?.setWatermarkVisible(this.watermarkOn()&&this.cameraRevealed)}revealCamera(){if(!this.shell||this.cameraRevealed)return;let e=this.estimateInput;if(!e)return;let r=te(e);r.width>0&&r.height>0&&requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.cameraRevealed||!this.shell||(this.cameraRevealed=!0,this.poseSource.type==="image"?this.shell.setMediaMode("image"):this.shell.setMediaMode("video"),this.applyWatermark())})})}setStatus(e){this.status=e,(e==="configuring"||e==="downloading"||e==="warming")&&this.emit({type:"initialization",step:e,message:e==="configuring"?"checking you plan and access":e==="downloading"?"downloading engine":"loading pose model",ready:!1}),this.notifyState()}setMode(e){this.mode!==e&&(this.mode=e,this.notifyState())}reportError(e){this.lastError=e,this.emit(e),this.notifyState()}notifyState(){this.stateListeners.forEach(e=>{try{e()}catch{}})}emit(e){if(this.listeners.forEach(n=>{try{n(e)}catch(u){this.opts.onDiagnostic?.(`[posetracker-web] listener threw on '${e.type}': `+(u instanceof Error?u.message:String(u)))}}),this.typedListeners.get(e.type)?.forEach(n=>{try{n(e)}catch{}}),this.typedListeners.get("*")?.forEach(n=>{try{n(e)}catch{}}),this.messageListeners.size===0)return;let i=Xe(e);this.messageListeners.forEach(n=>{try{n(i)}catch{}})}};function Le(t={}){return new Ie(t.apiToken,t)}function Tt(t,e={}){let r=Le(e);return r.mount(t),r}var ct={mount:Tt};return Ot(dr);})();
|
|
83
83
|
/*! Bundled license information:
|
|
84
84
|
|
|
85
85
|
js-sha256/src/sha256.js:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pose-tracker/pose-estimation-web",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "PoseTracker web pose estimation SDK (vanilla) — MoveNet via TensorFlow.js + remote model URL. Exercise engine via API token. No bundled weights.",
|
|
6
6
|
"type": "module",
|