@pose-tracker/react-native-pose-estimation-light 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/lib/PoseTrackerProvider.d.ts +3 -2
- package/lib/api/configure.d.ts +3 -2
- package/lib/api/configure.js +1 -1
- package/lib/client.js +29 -2
- package/lib/engine/types.d.ts +5 -0
- package/lib/engineChannel.d.ts +25 -3
- package/lib/engineChannel.js +34 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +8 -2
- package/lib/sdkVersion.d.ts +1 -1
- package/lib/sdkVersion.js +1 -1
- package/lib/types/manifest.d.ts +2 -2
- package/package.json +1 -1
- package/src/PoseTrackerProvider.tsx +3 -2
- package/src/api/configure.ts +4 -3
- package/src/client.ts +44 -4
- package/src/engine/types.ts +5 -0
- package/src/engineChannel.ts +45 -3
- package/src/index.ts +10 -1
- package/src/sdkVersion.ts +1 -1
- package/src/types/manifest.ts +2 -2
|
@@ -84,8 +84,9 @@ export interface PoseTrackerProviderProps {
|
|
|
84
84
|
/** Optional: without a token the SDK runs in keypoints-only mode. */
|
|
85
85
|
apiToken?: string;
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
87
|
+
* Remote engine. Default `'v4'`. Pass `'v3'` for the production FSM bundle.
|
|
88
|
+
* Unlabeled `startExercise('squat')` still uses the V3 squat FSM unless this
|
|
89
|
+
* is explicitly `'v4'`. Merged into `options.engine`; this prop wins.
|
|
89
90
|
*/
|
|
90
91
|
engine?: 'v3' | 'v4';
|
|
91
92
|
options?: PoseTrackerClientOptions;
|
package/lib/api/configure.d.ts
CHANGED
|
@@ -23,8 +23,9 @@ export interface ConfigureOptions {
|
|
|
23
23
|
/** Local cache versions, so the backend can answer `upToDate` per module. */
|
|
24
24
|
localVersions?: ConfigureRequest['localVersions'];
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Remote engine channel. Default `'v4'`. Pass `'v3'` for the production FSM
|
|
27
|
+
* bundle. Unlabeled `startExercise('squat')` stays on the V3 squat FSM even
|
|
28
|
+
* on the V4 channel unless `engine` is explicitly `'v4'`.
|
|
28
29
|
*/
|
|
29
30
|
engine?: EngineChannel;
|
|
30
31
|
}
|
package/lib/api/configure.js
CHANGED
|
@@ -37,7 +37,7 @@ async function configure(apiToken, options = {}) {
|
|
|
37
37
|
poseModelProfile: options.poseModelProfile ?? 'AdaptiveChoice',
|
|
38
38
|
locale: options.locale,
|
|
39
39
|
localVersions: options.localVersions,
|
|
40
|
-
engineChannel: options.engine === '
|
|
40
|
+
engineChannel: options.engine === 'v3' ? 'v3' : 'v4',
|
|
41
41
|
};
|
|
42
42
|
let response;
|
|
43
43
|
try {
|
package/lib/client.js
CHANGED
|
@@ -829,9 +829,29 @@ class PoseTrackerClient {
|
|
|
829
829
|
this.reportError({ type: 'error', code: 'invalid_exercise', message });
|
|
830
830
|
throw new Error(message);
|
|
831
831
|
}
|
|
832
|
-
|
|
832
|
+
const jumpId = (0, aliases_1.resolveExerciseName)(exerciseId);
|
|
833
|
+
if ((0, engineChannel_1.isJumpExercise)(jumpId)) {
|
|
834
|
+
const customs = this.getAvailableCustomExercises();
|
|
835
|
+
const custom = customs.find((e) => e.id === jumpId) ??
|
|
836
|
+
(0, aliases_1.findExerciseByIdOrAlias)(exerciseId, customs) ??
|
|
837
|
+
{
|
|
838
|
+
id: jumpId,
|
|
839
|
+
displayName: jumpId === 'jump_analysis' ? 'Jump Analysis' : 'Air Time Jump',
|
|
840
|
+
type: 'custom',
|
|
841
|
+
requiredParams: jumpId === 'jump_analysis' ? ['userHeightCm'] : [],
|
|
842
|
+
optionalParams: ['devicePitchDeg'],
|
|
843
|
+
};
|
|
844
|
+
this.startCustomExercise(custom, options);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
// Production squat stays on the V3 FSM unless engine is explicitly 'v4'.
|
|
848
|
+
const sessionEngine = (0, engineChannel_1.resolveMovementEngine)({
|
|
849
|
+
engine: this.options.engine,
|
|
850
|
+
exercise: jumpId || exerciseId,
|
|
851
|
+
});
|
|
852
|
+
if (sessionEngine === 'v4') {
|
|
833
853
|
const listed = this.engine.listExercises?.() ?? [];
|
|
834
|
-
const v4Hit = listed.find((e) => e.id === exerciseId);
|
|
854
|
+
const v4Hit = listed.find((e) => e.id === exerciseId) ?? listed.find((e) => e.id === jumpId);
|
|
835
855
|
if (v4Hit) {
|
|
836
856
|
this.beginEngineSession(this.v4ExerciseConfig(v4Hit), options);
|
|
837
857
|
return;
|
|
@@ -847,6 +867,12 @@ class PoseTrackerClient {
|
|
|
847
867
|
this.reportError({ type: 'error', code: 'invalid_exercise', message });
|
|
848
868
|
throw new Error(message);
|
|
849
869
|
}
|
|
870
|
+
// V4 bundle still runs the production V3 squat FSM unless engine is 'v4'.
|
|
871
|
+
if ((0, engineChannel_1.isProductionSquatId)(jumpId || exerciseId) &&
|
|
872
|
+
(0, engineChannel_1.normalizeEngineChannel)(this.options.engine) === 'v4') {
|
|
873
|
+
this.beginEngineSession(this.v4ExerciseConfig({ id: jumpId || 'squat', displayName: 'Squat', type: 'dynamic' }), options);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
850
876
|
const available = this.getAvailableExercises();
|
|
851
877
|
const exercise = (0, aliases_1.findExerciseByIdOrAlias)(exerciseId, available);
|
|
852
878
|
if (!exercise) {
|
|
@@ -879,6 +905,7 @@ class PoseTrackerClient {
|
|
|
879
905
|
difficulty: options.difficulty,
|
|
880
906
|
minGrade: this.features.minGrade ?? undefined,
|
|
881
907
|
debug,
|
|
908
|
+
v4CatalogSquat: this.options.engine === 'v4',
|
|
882
909
|
features: {
|
|
883
910
|
angles: this.features.angles,
|
|
884
911
|
recommendations: this.features.recommendations,
|
package/lib/engine/types.d.ts
CHANGED
|
@@ -41,6 +41,11 @@ export interface EngineSessionOptions {
|
|
|
41
41
|
* Ignored by older engines.
|
|
42
42
|
*/
|
|
43
43
|
debug?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Run the V4 catalog squat instead of the production V3 FSM.
|
|
46
|
+
* Set when the host passed `engine: 'v4'` explicitly.
|
|
47
|
+
*/
|
|
48
|
+
v4CatalogSquat?: boolean;
|
|
44
49
|
}
|
|
45
50
|
export interface EngineSession {
|
|
46
51
|
/** Feed one estimated pose; the session emits events through the sink. */
|
package/lib/engineChannel.d.ts
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Engine channel + production squat lock.
|
|
3
|
+
*
|
|
4
|
+
* Default handshake is V4 except unlabeled squat / face_squat (V3 FSM).
|
|
5
|
+
* Explicit `engine: 'v4'` is the only way to run the V4 catalog squat.
|
|
6
|
+
* Explicit `engine: 'v3'` keeps the full V3 bundle.
|
|
7
|
+
* See engine/v4/catalog/SQUAT_PRODUCTION.md
|
|
8
|
+
*/
|
|
2
9
|
export type EngineChannel = 'v3' | 'v4';
|
|
3
|
-
/**
|
|
4
|
-
export declare const V4_ONLY_EXERCISE_IDS: readonly ["shoulder_roll", "shoulder_deep_breath", "chair_forward_fold"];
|
|
10
|
+
/** Wellness ids with no V3 FSM. */
|
|
11
|
+
export declare const V4_ONLY_EXERCISE_IDS: readonly ["shoulder_roll", "shoulder_deep_breath", "chair_forward_fold", "chair_side_stretch"];
|
|
12
|
+
/** Existing jump handlers — not V4 JSON. */
|
|
13
|
+
export declare const JUMP_EXERCISE_IDS: readonly ["jump_analysis", "air_time_jump"];
|
|
14
|
+
export declare const PRODUCTION_SQUAT_IDS: readonly ["squat", "face_squat"];
|
|
15
|
+
export declare function isProductionSquatId(exerciseId?: string | null): boolean;
|
|
16
|
+
/** Handshake default is V4. Pass `'v3'` to force the V3 bundle. */
|
|
5
17
|
export declare function normalizeEngineChannel(raw?: string | null): EngineChannel;
|
|
18
|
+
/**
|
|
19
|
+
* Session interpreter for this id. Production squat stays V3 unless `engine`
|
|
20
|
+
* is explicitly `'v4'`. Other ids follow the handshake (default V4).
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveMovementEngine(opts?: {
|
|
23
|
+
engine?: string | null;
|
|
24
|
+
exercise?: string | null;
|
|
25
|
+
userExercise?: boolean;
|
|
26
|
+
}): EngineChannel;
|
|
6
27
|
export declare function requiresEngineV4(exerciseId: string): boolean;
|
|
28
|
+
export declare function isJumpExercise(exerciseId: string): boolean;
|
package/lib/engineChannel.js
CHANGED
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.V4_ONLY_EXERCISE_IDS = void 0;
|
|
3
|
+
exports.PRODUCTION_SQUAT_IDS = exports.JUMP_EXERCISE_IDS = exports.V4_ONLY_EXERCISE_IDS = void 0;
|
|
4
|
+
exports.isProductionSquatId = isProductionSquatId;
|
|
4
5
|
exports.normalizeEngineChannel = normalizeEngineChannel;
|
|
6
|
+
exports.resolveMovementEngine = resolveMovementEngine;
|
|
5
7
|
exports.requiresEngineV4 = requiresEngineV4;
|
|
6
|
-
|
|
8
|
+
exports.isJumpExercise = isJumpExercise;
|
|
9
|
+
/** Wellness ids with no V3 FSM. */
|
|
7
10
|
exports.V4_ONLY_EXERCISE_IDS = [
|
|
8
11
|
'shoulder_roll',
|
|
9
12
|
'shoulder_deep_breath',
|
|
10
13
|
'chair_forward_fold',
|
|
14
|
+
'chair_side_stretch',
|
|
11
15
|
];
|
|
16
|
+
/** Existing jump handlers — not V4 JSON. */
|
|
17
|
+
exports.JUMP_EXERCISE_IDS = ['jump_analysis', 'air_time_jump'];
|
|
18
|
+
exports.PRODUCTION_SQUAT_IDS = ['squat', 'face_squat'];
|
|
19
|
+
function isProductionSquatId(exerciseId) {
|
|
20
|
+
const id = String(exerciseId || '')
|
|
21
|
+
.trim()
|
|
22
|
+
.toLowerCase();
|
|
23
|
+
return id === 'squat' || id === 'face_squat';
|
|
24
|
+
}
|
|
25
|
+
/** Handshake default is V4. Pass `'v3'` to force the V3 bundle. */
|
|
12
26
|
function normalizeEngineChannel(raw) {
|
|
13
|
-
return raw === '
|
|
27
|
+
return raw === 'v3' ? 'v3' : 'v4';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Session interpreter for this id. Production squat stays V3 unless `engine`
|
|
31
|
+
* is explicitly `'v4'`. Other ids follow the handshake (default V4).
|
|
32
|
+
*/
|
|
33
|
+
function resolveMovementEngine(opts = {}) {
|
|
34
|
+
if (opts.userExercise)
|
|
35
|
+
return 'v4';
|
|
36
|
+
const requested = String(opts.engine || '')
|
|
37
|
+
.trim()
|
|
38
|
+
.toLowerCase();
|
|
39
|
+
if (isProductionSquatId(opts.exercise) && requested !== 'v4')
|
|
40
|
+
return 'v3';
|
|
41
|
+
return normalizeEngineChannel(opts.engine);
|
|
14
42
|
}
|
|
15
43
|
function requiresEngineV4(exerciseId) {
|
|
16
44
|
return exports.V4_ONLY_EXERCISE_IDS.includes(exerciseId);
|
|
17
45
|
}
|
|
46
|
+
function isJumpExercise(exerciseId) {
|
|
47
|
+
return exports.JUMP_EXERCISE_IDS.includes(exerciseId);
|
|
48
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type { PoseModelAlias, PoseModelKind, ResolvePoseModelOptions, ResolvedPo
|
|
|
26
26
|
export { PoseTrackerClient } from './client';
|
|
27
27
|
export type { PoseTrackerClientOptions, PreferredBackend, StartExerciseOptions } from './client';
|
|
28
28
|
export type { EngineChannel } from './engineChannel';
|
|
29
|
-
export { V4_ONLY_EXERCISE_IDS, normalizeEngineChannel, requiresEngineV4 } from './engineChannel';
|
|
29
|
+
export { JUMP_EXERCISE_IDS, PRODUCTION_SQUAT_IDS, V4_ONLY_EXERCISE_IDS, isJumpExercise, isProductionSquatId, normalizeEngineChannel, requiresEngineV4, resolveMovementEngine, } from './engineChannel';
|
|
30
30
|
export type { ColdStartMode, PreloadOptions } from './types/preload';
|
|
31
31
|
export type { SkeletonAnglesStyle, SkeletonCirclesStyle, SkeletonDefinition, SkeletonLinesStyle, } from './types/skeleton';
|
|
32
32
|
export { DEFAULT_SKELETON_DEFINITION } from './types/skeleton';
|
package/lib/index.js
CHANGED
|
@@ -28,8 +28,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
28
28
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
29
29
|
};
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.
|
|
32
|
-
exports.
|
|
31
|
+
exports.isExpoGo = exports.PoseCameraView = exports.VISION_JOINT_TO_SDK = exports.poseFromVisionPluginResult = exports.VISION_BODY_POSE_PLUGIN_NAME = exports.isVisionPoseCameraAvailable = exports.isVisionBackendAvailable = exports.VisionUnavailableError = exports.VisionPoseBackend = exports.POSETRACKER_LOGO_DATA_URL = exports.POSE_HTML_BUILD = exports.DEFAULT_LOADING_TEXT = exports.buildPoseHtml = exports.WebViewPoseView = exports.isWebViewPoseBackend = exports.WebViewPoseBackend = exports.usePoseTracker = exports.PoseTrackerProvider = exports.SkeletonFetchError = exports.fetchSkeletonDefinition = exports.DEFAULT_SKELETON_DEFINITION = exports.resolveMovementEngine = exports.requiresEngineV4 = exports.normalizeEngineChannel = exports.isProductionSquatId = exports.isJumpExercise = exports.V4_ONLY_EXERCISE_IDS = exports.PRODUCTION_SQUAT_IDS = exports.JUMP_EXERCISE_IDS = exports.PoseTrackerClient = exports.resolvePoseModel = exports.originFromModelUrl = exports.DEFAULT_MOVENET_LIGHTNING_URL = exports.ONLINE_POSE_DETECTION_VERSION = exports.ONLINE_TFJS_VERSION = exports.defaultPoseDetectionCdnUrl = exports.defaultOnlineRuntimeUrls = exports.getOnlineRuntimeVersion = exports.getOnlineRuntimeParts = exports.RuntimeCache = exports.TrackError = exports.UsageTracker = exports.resolveExerciseName = exports.findExerciseByIdOrAlias = exports.EXERCISE_ALIASES = exports.SDK_VERSION = exports.SDK_NAME = exports.DEFAULT_BASE_URL = exports.ConfigureError = exports.configure = void 0;
|
|
32
|
+
exports.COMBINED_REFERENCE_EXERCISE_MESSAGE = exports.INVALID_TOKEN_MESSAGE = exports.FREE_PLAN_FEATURES_MESSAGE = exports.DEFAULT_FEATURES = exports.RuntimeGuard = exports.scoreDeviceCapability = exports.profileFromScore = exports.isMaliRenderer = exports.ENABLE_MALI_HARD_CAP = exports.CAPTURE_CONSTRAINT_MODE = exports.ANDROID_SOFT_CAP_PROFILE = exports.ANDROID_PREPROCESS_PATH = exports.ANDROID_PERF_DEBUG = exports.ANDROID_MIN_TARGET_FPS = exports.ANDROID_INFER_FRAME_SKIP = exports.profileFromWarmupMedianMs = exports.nextLowerQualityProfile = exports.minMedianMsForMinTarget = exports.lowerQualityProfile = exports.isQualityProfileId = exports.getQualityProfile = exports.getMinTargetFps = exports.getIdealFpsRange = exports.getCriticalFpsThreshold = exports.estimatedFpsFromMedianMs = exports.currentQualityPlatform = exports.TARGET_MEDIAN_MS = exports.TARGET_FPS = exports.QUALITY_PROFILES = exports.QUALITY_LADDER = exports.MIN_TARGET_FPS_IOS = exports.MIN_TARGET_FPS_DEFAULT = exports.MIN_TARGET_FPS_ANDROID = exports.QUALITY_SETTLE_MS = exports.LOW_FPS_STREAK_BEFORE_DOWNGRADE = exports.IDEAL_FPS_RANGE_IOS = exports.IDEAL_FPS_RANGE_ANDROID = exports.CRITICAL_FPS_THRESHOLD = exports.AdaptiveQualityController = exports.toClassicNativeMessage = exports.logPlatformBanner = exports.logFrameStats = exports.logAccelerationReport = exports.defaultDiagnosticLogger = exports.createNativeFileStore = exports.createMemoryFileStore = exports.createBlobUtilFileStore = exports.createRnfsFileStore = exports.createExpoFileStore = exports.EngineLoader = void 0;
|
|
33
|
+
exports.resolveFeatures = exports.shouldShowWatermark = exports.isPaidPlan = exports.freeBlockedFeatures = exports.featureNotSupportedMessage = void 0;
|
|
33
34
|
// Handshake + usage tracking
|
|
34
35
|
var configure_1 = require("./api/configure");
|
|
35
36
|
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return configure_1.configure; } });
|
|
@@ -62,9 +63,14 @@ Object.defineProperty(exports, "resolvePoseModel", { enumerable: true, get: func
|
|
|
62
63
|
var client_1 = require("./client");
|
|
63
64
|
Object.defineProperty(exports, "PoseTrackerClient", { enumerable: true, get: function () { return client_1.PoseTrackerClient; } });
|
|
64
65
|
var engineChannel_1 = require("./engineChannel");
|
|
66
|
+
Object.defineProperty(exports, "JUMP_EXERCISE_IDS", { enumerable: true, get: function () { return engineChannel_1.JUMP_EXERCISE_IDS; } });
|
|
67
|
+
Object.defineProperty(exports, "PRODUCTION_SQUAT_IDS", { enumerable: true, get: function () { return engineChannel_1.PRODUCTION_SQUAT_IDS; } });
|
|
65
68
|
Object.defineProperty(exports, "V4_ONLY_EXERCISE_IDS", { enumerable: true, get: function () { return engineChannel_1.V4_ONLY_EXERCISE_IDS; } });
|
|
69
|
+
Object.defineProperty(exports, "isJumpExercise", { enumerable: true, get: function () { return engineChannel_1.isJumpExercise; } });
|
|
70
|
+
Object.defineProperty(exports, "isProductionSquatId", { enumerable: true, get: function () { return engineChannel_1.isProductionSquatId; } });
|
|
66
71
|
Object.defineProperty(exports, "normalizeEngineChannel", { enumerable: true, get: function () { return engineChannel_1.normalizeEngineChannel; } });
|
|
67
72
|
Object.defineProperty(exports, "requiresEngineV4", { enumerable: true, get: function () { return engineChannel_1.requiresEngineV4; } });
|
|
73
|
+
Object.defineProperty(exports, "resolveMovementEngine", { enumerable: true, get: function () { return engineChannel_1.resolveMovementEngine; } });
|
|
68
74
|
var skeleton_1 = require("./types/skeleton");
|
|
69
75
|
Object.defineProperty(exports, "DEFAULT_SKELETON_DEFINITION", { enumerable: true, get: function () { return skeleton_1.DEFAULT_SKELETON_DEFINITION; } });
|
|
70
76
|
var skeleton_2 = require("./api/skeleton");
|
package/lib/sdkVersion.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** AUTO-GENERATED by scripts/sync-sdk-version.mjs — do not edit. */
|
|
2
|
-
export declare const SDK_VERSION = "0.3.
|
|
2
|
+
export declare const SDK_VERSION = "0.3.1";
|
package/lib/sdkVersion.js
CHANGED
package/lib/types/manifest.d.ts
CHANGED
|
@@ -169,8 +169,8 @@ export interface ConfigureRequest {
|
|
|
169
169
|
engine?: string | null;
|
|
170
170
|
};
|
|
171
171
|
/**
|
|
172
|
-
* Which remote engine to download.
|
|
173
|
-
*
|
|
172
|
+
* Which remote engine to download. New SDKs send `'v4'` by default.
|
|
173
|
+
* Omitted (0.2.x) still means V3 on the server. `'v3'` forces the FSM bundle.
|
|
174
174
|
*/
|
|
175
175
|
engineChannel?: 'v3' | 'v4';
|
|
176
176
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pose-tracker/react-native-pose-estimation-light",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Human pose estimation SDK for React Native (light / online) — MoveNet Lightning via CDN + model URL each session, no bundled weights. iOS & Android (Expo Go). Optional API-key exercise engine.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -107,8 +107,9 @@ export interface PoseTrackerProviderProps {
|
|
|
107
107
|
/** Optional: without a token the SDK runs in keypoints-only mode. */
|
|
108
108
|
apiToken?: string;
|
|
109
109
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
110
|
+
* Remote engine. Default `'v4'`. Pass `'v3'` for the production FSM bundle.
|
|
111
|
+
* Unlabeled `startExercise('squat')` still uses the V3 squat FSM unless this
|
|
112
|
+
* is explicitly `'v4'`. Merged into `options.engine`; this prop wins.
|
|
112
113
|
*/
|
|
113
114
|
engine?: 'v3' | 'v4';
|
|
114
115
|
options?: PoseTrackerClientOptions;
|
package/src/api/configure.ts
CHANGED
|
@@ -30,8 +30,9 @@ export interface ConfigureOptions {
|
|
|
30
30
|
/** Local cache versions, so the backend can answer `upToDate` per module. */
|
|
31
31
|
localVersions?: ConfigureRequest['localVersions'];
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
33
|
+
* Remote engine channel. Default `'v4'`. Pass `'v3'` for the production FSM
|
|
34
|
+
* bundle. Unlabeled `startExercise('squat')` stays on the V3 squat FSM even
|
|
35
|
+
* on the V4 channel unless `engine` is explicitly `'v4'`.
|
|
35
36
|
*/
|
|
36
37
|
engine?: EngineChannel;
|
|
37
38
|
}
|
|
@@ -59,7 +60,7 @@ export async function configure(
|
|
|
59
60
|
poseModelProfile: options.poseModelProfile ?? 'AdaptiveChoice',
|
|
60
61
|
locale: options.locale,
|
|
61
62
|
localVersions: options.localVersions,
|
|
62
|
-
engineChannel: options.engine === '
|
|
63
|
+
engineChannel: options.engine === 'v3' ? 'v3' : 'v4',
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
let response: Response;
|
package/src/client.ts
CHANGED
|
@@ -37,8 +37,15 @@ import type { CustomExerciseDescriptor, EngineSession, PoseTrackerEngine } from
|
|
|
37
37
|
import type { PoseBackend, PoseInputFrame } from './backends/PoseBackend';
|
|
38
38
|
import { VisionPoseBackend } from './backends/vision/VisionPoseBackend';
|
|
39
39
|
import { WebViewPoseBackend } from './backends/webview/WebViewPoseBackend';
|
|
40
|
-
import { findExerciseByIdOrAlias } from './exercises/aliases';
|
|
41
|
-
import {
|
|
40
|
+
import { findExerciseByIdOrAlias, resolveExerciseName } from './exercises/aliases';
|
|
41
|
+
import {
|
|
42
|
+
isJumpExercise,
|
|
43
|
+
isProductionSquatId,
|
|
44
|
+
normalizeEngineChannel,
|
|
45
|
+
requiresEngineV4,
|
|
46
|
+
resolveMovementEngine,
|
|
47
|
+
type EngineChannel,
|
|
48
|
+
} from './engineChannel';
|
|
42
49
|
import { fetchSkeletonDefinition } from './api/skeleton';
|
|
43
50
|
import type { ExerciseConfig, ModelDescriptor, SdkManifest } from './types/manifest';
|
|
44
51
|
import type { ColdStartMode, PreloadOptions } from './types/preload';
|
|
@@ -1059,9 +1066,30 @@ export class PoseTrackerClient {
|
|
|
1059
1066
|
this.reportError({ type: 'error', code: 'invalid_exercise', message });
|
|
1060
1067
|
throw new Error(message);
|
|
1061
1068
|
}
|
|
1062
|
-
|
|
1069
|
+
const jumpId = resolveExerciseName(exerciseId);
|
|
1070
|
+
if (isJumpExercise(jumpId)) {
|
|
1071
|
+
const customs = this.getAvailableCustomExercises();
|
|
1072
|
+
const custom =
|
|
1073
|
+
customs.find((e) => e.id === jumpId) ??
|
|
1074
|
+
findExerciseByIdOrAlias(exerciseId, customs) ??
|
|
1075
|
+
{
|
|
1076
|
+
id: jumpId,
|
|
1077
|
+
displayName: jumpId === 'jump_analysis' ? 'Jump Analysis' : 'Air Time Jump',
|
|
1078
|
+
type: 'custom' as const,
|
|
1079
|
+
requiredParams: jumpId === 'jump_analysis' ? ['userHeightCm'] : [],
|
|
1080
|
+
optionalParams: ['devicePitchDeg'],
|
|
1081
|
+
};
|
|
1082
|
+
this.startCustomExercise(custom, options);
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
// Production squat stays on the V3 FSM unless engine is explicitly 'v4'.
|
|
1086
|
+
const sessionEngine = resolveMovementEngine({
|
|
1087
|
+
engine: this.options.engine,
|
|
1088
|
+
exercise: jumpId || exerciseId,
|
|
1089
|
+
});
|
|
1090
|
+
if (sessionEngine === 'v4') {
|
|
1063
1091
|
const listed = this.engine.listExercises?.() ?? [];
|
|
1064
|
-
const v4Hit = listed.find((e) => e.id === exerciseId);
|
|
1092
|
+
const v4Hit = listed.find((e) => e.id === exerciseId) ?? listed.find((e) => e.id === jumpId);
|
|
1065
1093
|
if (v4Hit) {
|
|
1066
1094
|
this.beginEngineSession(this.v4ExerciseConfig(v4Hit), options);
|
|
1067
1095
|
return;
|
|
@@ -1078,6 +1106,17 @@ export class PoseTrackerClient {
|
|
|
1078
1106
|
this.reportError({ type: 'error', code: 'invalid_exercise', message });
|
|
1079
1107
|
throw new Error(message);
|
|
1080
1108
|
}
|
|
1109
|
+
// V4 bundle still runs the production V3 squat FSM unless engine is 'v4'.
|
|
1110
|
+
if (
|
|
1111
|
+
isProductionSquatId(jumpId || exerciseId) &&
|
|
1112
|
+
normalizeEngineChannel(this.options.engine) === 'v4'
|
|
1113
|
+
) {
|
|
1114
|
+
this.beginEngineSession(
|
|
1115
|
+
this.v4ExerciseConfig({ id: jumpId || 'squat', displayName: 'Squat', type: 'dynamic' }),
|
|
1116
|
+
options,
|
|
1117
|
+
);
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1081
1120
|
const available = this.getAvailableExercises();
|
|
1082
1121
|
const exercise = findExerciseByIdOrAlias(exerciseId, available);
|
|
1083
1122
|
if (!exercise) {
|
|
@@ -1112,6 +1151,7 @@ export class PoseTrackerClient {
|
|
|
1112
1151
|
difficulty: options.difficulty,
|
|
1113
1152
|
minGrade: this.features.minGrade ?? undefined,
|
|
1114
1153
|
debug,
|
|
1154
|
+
v4CatalogSquat: this.options.engine === 'v4',
|
|
1115
1155
|
features: {
|
|
1116
1156
|
angles: this.features.angles,
|
|
1117
1157
|
recommendations: this.features.recommendations,
|
package/src/engine/types.ts
CHANGED
|
@@ -44,6 +44,11 @@ export interface EngineSessionOptions {
|
|
|
44
44
|
* Ignored by older engines.
|
|
45
45
|
*/
|
|
46
46
|
debug?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Run the V4 catalog squat instead of the production V3 FSM.
|
|
49
|
+
* Set when the host passed `engine: 'v4'` explicitly.
|
|
50
|
+
*/
|
|
51
|
+
v4CatalogSquat?: boolean;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
export interface EngineSession {
|
package/src/engineChannel.ts
CHANGED
|
@@ -1,17 +1,59 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Engine channel + production squat lock.
|
|
3
|
+
*
|
|
4
|
+
* Default handshake is V4 except unlabeled squat / face_squat (V3 FSM).
|
|
5
|
+
* Explicit `engine: 'v4'` is the only way to run the V4 catalog squat.
|
|
6
|
+
* Explicit `engine: 'v3'` keeps the full V3 bundle.
|
|
7
|
+
* See engine/v4/catalog/SQUAT_PRODUCTION.md
|
|
8
|
+
*/
|
|
2
9
|
export type EngineChannel = 'v3' | 'v4';
|
|
3
10
|
|
|
4
|
-
/**
|
|
11
|
+
/** Wellness ids with no V3 FSM. */
|
|
5
12
|
export const V4_ONLY_EXERCISE_IDS = [
|
|
6
13
|
'shoulder_roll',
|
|
7
14
|
'shoulder_deep_breath',
|
|
8
15
|
'chair_forward_fold',
|
|
16
|
+
'chair_side_stretch',
|
|
9
17
|
] as const;
|
|
10
18
|
|
|
19
|
+
/** Existing jump handlers — not V4 JSON. */
|
|
20
|
+
export const JUMP_EXERCISE_IDS = ['jump_analysis', 'air_time_jump'] as const;
|
|
21
|
+
|
|
22
|
+
export const PRODUCTION_SQUAT_IDS = ['squat', 'face_squat'] as const;
|
|
23
|
+
|
|
24
|
+
export function isProductionSquatId(exerciseId?: string | null): boolean {
|
|
25
|
+
const id = String(exerciseId || '')
|
|
26
|
+
.trim()
|
|
27
|
+
.toLowerCase();
|
|
28
|
+
return id === 'squat' || id === 'face_squat';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Handshake default is V4. Pass `'v3'` to force the V3 bundle. */
|
|
11
32
|
export function normalizeEngineChannel(raw?: string | null): EngineChannel {
|
|
12
|
-
return raw === '
|
|
33
|
+
return raw === 'v3' ? 'v3' : 'v4';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Session interpreter for this id. Production squat stays V3 unless `engine`
|
|
38
|
+
* is explicitly `'v4'`. Other ids follow the handshake (default V4).
|
|
39
|
+
*/
|
|
40
|
+
export function resolveMovementEngine(opts: {
|
|
41
|
+
engine?: string | null;
|
|
42
|
+
exercise?: string | null;
|
|
43
|
+
userExercise?: boolean;
|
|
44
|
+
} = {}): EngineChannel {
|
|
45
|
+
if (opts.userExercise) return 'v4';
|
|
46
|
+
const requested = String(opts.engine || '')
|
|
47
|
+
.trim()
|
|
48
|
+
.toLowerCase();
|
|
49
|
+
if (isProductionSquatId(opts.exercise) && requested !== 'v4') return 'v3';
|
|
50
|
+
return normalizeEngineChannel(opts.engine);
|
|
13
51
|
}
|
|
14
52
|
|
|
15
53
|
export function requiresEngineV4(exerciseId: string): boolean {
|
|
16
54
|
return (V4_ONLY_EXERCISE_IDS as readonly string[]).includes(exerciseId);
|
|
17
55
|
}
|
|
56
|
+
|
|
57
|
+
export function isJumpExercise(exerciseId: string): boolean {
|
|
58
|
+
return (JUMP_EXERCISE_IDS as readonly string[]).includes(exerciseId);
|
|
59
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -60,7 +60,16 @@ export type {
|
|
|
60
60
|
export { PoseTrackerClient } from './client';
|
|
61
61
|
export type { PoseTrackerClientOptions, PreferredBackend, StartExerciseOptions } from './client';
|
|
62
62
|
export type { EngineChannel } from './engineChannel';
|
|
63
|
-
export {
|
|
63
|
+
export {
|
|
64
|
+
JUMP_EXERCISE_IDS,
|
|
65
|
+
PRODUCTION_SQUAT_IDS,
|
|
66
|
+
V4_ONLY_EXERCISE_IDS,
|
|
67
|
+
isJumpExercise,
|
|
68
|
+
isProductionSquatId,
|
|
69
|
+
normalizeEngineChannel,
|
|
70
|
+
requiresEngineV4,
|
|
71
|
+
resolveMovementEngine,
|
|
72
|
+
} from './engineChannel';
|
|
64
73
|
export type { ColdStartMode, PreloadOptions } from './types/preload';
|
|
65
74
|
export type {
|
|
66
75
|
SkeletonAnglesStyle,
|
package/src/sdkVersion.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** AUTO-GENERATED by scripts/sync-sdk-version.mjs — do not edit. */
|
|
2
|
-
export const SDK_VERSION = '0.3.
|
|
2
|
+
export const SDK_VERSION = '0.3.1';
|
package/src/types/manifest.ts
CHANGED
|
@@ -200,8 +200,8 @@ export interface ConfigureRequest {
|
|
|
200
200
|
engine?: string | null;
|
|
201
201
|
};
|
|
202
202
|
/**
|
|
203
|
-
* Which remote engine to download.
|
|
204
|
-
*
|
|
203
|
+
* Which remote engine to download. New SDKs send `'v4'` by default.
|
|
204
|
+
* Omitted (0.2.x) still means V3 on the server. `'v3'` forces the FSM bundle.
|
|
205
205
|
*/
|
|
206
206
|
engineChannel?: 'v3' | 'v4';
|
|
207
207
|
}
|