@lovelace_lol/embody 0.0.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/LICENSE +21 -0
- package/README.md +2411 -0
- package/dist/authoring/boneAuthoring.d.ts +132 -0
- package/dist/camera/annotationCameraAngles.d.ts +28 -0
- package/dist/camera/cameraRelativeGaze.d.ts +15 -0
- package/dist/camera/rustAnnotationCameraCore.d.ts +136 -0
- package/dist/cljs.cjs +362 -0
- package/dist/cljs.cjs.map +1 -0
- package/dist/cljs.d.ts +5 -0
- package/dist/cljs.js +359 -0
- package/dist/cljs.js.map +1 -0
- package/dist/core/TsClipCompiler.d.ts +51 -0
- package/dist/core/TsRuntimeCore.d.ts +40 -0
- package/dist/core/WasmRuntimeCore.d.ts +55 -0
- package/dist/core/compositeAxis.d.ts +4 -0
- package/dist/core/contracts/abi.d.ts +70 -0
- package/dist/core/contracts/clip.d.ts +44 -0
- package/dist/core/contracts/frame.d.ts +27 -0
- package/dist/core/contracts/host.d.ts +28 -0
- package/dist/core/contracts/ids.d.ts +15 -0
- package/dist/core/contracts/index.d.ts +10 -0
- package/dist/core/contracts/model.d.ts +31 -0
- package/dist/core/contracts/primitives.d.ts +17 -0
- package/dist/core/contracts/skeletonFit.d.ts +41 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/types.d.ts +472 -0
- package/dist/core.cjs +1065 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.js +1040 -0
- package/dist/core.js.map +1 -0
- package/dist/engines/three/Embody.d.ts +373 -0
- package/dist/engines/three/ThreeAnimationRuntime.d.ts +203 -0
- package/dist/engines/three/ThreeClipAdapter.d.ts +24 -0
- package/dist/engines/three/ThreeFrameApplier.d.ts +68 -0
- package/dist/engines/three/ThreeModelInspector.d.ts +54 -0
- package/dist/engines/three/bakedClipPartitioning.d.ts +16 -0
- package/dist/engines/three/balanceUtils.d.ts +3 -0
- package/dist/engines/three/characterHost.d.ts +80 -0
- package/dist/engines/three/characterScene.d.ts +98 -0
- package/dist/engines/three/hair/HairPhysicsController.d.ts +124 -0
- package/dist/engines/three/index.d.ts +20 -0
- package/dist/engines/three/modelLoader.d.ts +61 -0
- package/dist/engines/three/profileTestScene.d.ts +52 -0
- package/dist/engines/three/types.d.ts +20 -0
- package/dist/hair/appearance.d.ts +29 -0
- package/dist/index.cjs +15431 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.js +15141 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/Animation.d.ts +315 -0
- package/dist/interfaces/EmbodyRuntime.d.ts +90 -0
- package/dist/interfaces/Hair.d.ts +181 -0
- package/dist/interfaces/HairPhysics.d.ts +90 -0
- package/dist/interfaces/index.d.ts +11 -0
- package/dist/mappings/extendPresetWithProfile.d.ts +17 -0
- package/dist/mappings/types.d.ts +309 -0
- package/dist/mappings/visemeSystem.d.ts +26 -0
- package/dist/physics/HairPhysics.d.ts +86 -0
- package/dist/physics/RustHairPhysics.d.ts +18 -0
- package/dist/physics/index.d.ts +6 -0
- package/dist/preset.cjs +2000 -0
- package/dist/preset.cjs.map +1 -0
- package/dist/preset.js +1997 -0
- package/dist/preset.js.map +1 -0
- package/dist/presets/bettaFish.d.ts +328 -0
- package/dist/presets/cc4.d.ts +145 -0
- package/dist/presets/index.d.ts +27 -0
- package/dist/profiles/resolveProfileConfig.d.ts +36 -0
- package/dist/profiles/types.d.ts +118 -0
- package/dist/regions/normalizeRegionTree.d.ts +6 -0
- package/dist/regions/regionMapping.d.ts +17 -0
- package/dist/regions/types.d.ts +122 -0
- package/dist/skeletonTemplates/humanoidSkeletonTemplates.d.ts +32 -0
- package/dist/test/setupWasm.d.ts +1 -0
- package/dist/three.cjs +10115 -0
- package/dist/three.cjs.map +1 -0
- package/dist/three.js +10086 -0
- package/dist/three.js.map +1 -0
- package/dist/validation/analyzeModel.d.ts +68 -0
- package/dist/validation/extractModelData.d.ts +90 -0
- package/dist/validation/generateMappingCorrections.d.ts +32 -0
- package/dist/validation/geometryHelpers.d.ts +66 -0
- package/dist/validation/types.d.ts +10 -0
- package/dist/validation/validateMappings.d.ts +77 -0
- package/dist/wasm/LICENSE +21 -0
- package/dist/wasm/README.md +72 -0
- package/dist/wasm/embody_wasm.d.ts +531 -0
- package/dist/wasm/embody_wasm.js +1635 -0
- package/dist/wasm/embody_wasm_bg.wasm +0 -0
- package/dist/wasm/embody_wasm_bg.wasm.d.ts +107 -0
- package/dist/wasm.cjs +302 -0
- package/dist/wasm.cjs.map +1 -0
- package/dist/wasm.d.ts +43 -0
- package/dist/wasm.js +268 -0
- package/dist/wasm.js.map +1 -0
- package/dist/wasmTypes.d.ts +121 -0
- package/package.json +99 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { AUInfo } from '../core/types';
|
|
2
|
+
import type { Profile } from '../mappings/types';
|
|
3
|
+
export declare const JOINT_CONTROL_SECTION = "Joint Controls";
|
|
4
|
+
export type BoneAxisKey = 'pitch' | 'yaw' | 'roll';
|
|
5
|
+
export type RotationChannel = 'rx' | 'ry' | 'rz';
|
|
6
|
+
export type BoneAxisDirection = 'negative' | 'positive';
|
|
7
|
+
export type BoneAxisDirectionScale = -1 | 1;
|
|
8
|
+
export interface BilateralAxisDirectionScaleState {
|
|
9
|
+
left: BoneAxisDirectionScale | null;
|
|
10
|
+
right: BoneAxisDirectionScale | null;
|
|
11
|
+
}
|
|
12
|
+
export interface BoneAxisBindingState {
|
|
13
|
+
axis: BoneAxisKey;
|
|
14
|
+
channel: RotationChannel;
|
|
15
|
+
nodeKey: string;
|
|
16
|
+
positiveAuId: number | null;
|
|
17
|
+
negativeAuId: number | null;
|
|
18
|
+
positiveMaxDegrees: number | null;
|
|
19
|
+
negativeMaxDegrees: number | null;
|
|
20
|
+
positiveScale: BoneAxisDirectionScale | null;
|
|
21
|
+
negativeScale: BoneAxisDirectionScale | null;
|
|
22
|
+
}
|
|
23
|
+
export interface BoneAxisBindingUpdate {
|
|
24
|
+
channel?: RotationChannel;
|
|
25
|
+
positiveAuId?: number | null;
|
|
26
|
+
negativeAuId?: number | null;
|
|
27
|
+
positiveMaxDegrees?: number | null;
|
|
28
|
+
negativeMaxDegrees?: number | null;
|
|
29
|
+
positiveScale?: BoneAxisDirectionScale | null;
|
|
30
|
+
negativeScale?: BoneAxisDirectionScale | null;
|
|
31
|
+
positiveLeftScale?: BoneAxisDirectionScale | null;
|
|
32
|
+
positiveRightScale?: BoneAxisDirectionScale | null;
|
|
33
|
+
negativeLeftScale?: BoneAxisDirectionScale | null;
|
|
34
|
+
negativeRightScale?: BoneAxisDirectionScale | null;
|
|
35
|
+
}
|
|
36
|
+
export declare function isMaxDegreesOnlyAxisBindingUpdate(update: BoneAxisBindingUpdate): boolean;
|
|
37
|
+
export interface AUBoneBindingState {
|
|
38
|
+
auId: number;
|
|
39
|
+
boneName: string | null;
|
|
40
|
+
nodeKey: string | null;
|
|
41
|
+
axis: BoneAxisKey;
|
|
42
|
+
direction: BoneAxisDirection;
|
|
43
|
+
channel: RotationChannel | null;
|
|
44
|
+
maxDegrees: number | null;
|
|
45
|
+
oppositeAuId: number | null;
|
|
46
|
+
oppositeMaxDegrees: number | null;
|
|
47
|
+
hasBinding: boolean;
|
|
48
|
+
hasMultipleBindings: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface CreatedBoneAxisAu {
|
|
51
|
+
auId: number;
|
|
52
|
+
profile: Profile;
|
|
53
|
+
}
|
|
54
|
+
export interface ChiralBoneNamePair {
|
|
55
|
+
leftBoneName: string;
|
|
56
|
+
rightBoneName: string;
|
|
57
|
+
selectedSide: 'left' | 'right';
|
|
58
|
+
familyLabel: string;
|
|
59
|
+
}
|
|
60
|
+
export type BoneControlScope = 'Left Eye Only' | 'Right Eye Only' | 'Both Eyes';
|
|
61
|
+
export type BoneControlFamily = 'Horizontal Gaze' | 'Vertical Gaze';
|
|
62
|
+
export type BilateralBoneAxisScope = 'shared' | 'left' | 'right';
|
|
63
|
+
export interface BilateralAxisScopeBindingState {
|
|
64
|
+
scope: BilateralBoneAxisScope;
|
|
65
|
+
label: string;
|
|
66
|
+
negativeAuId: number | null;
|
|
67
|
+
positiveAuId: number | null;
|
|
68
|
+
negativeMaxDegrees: number | null;
|
|
69
|
+
positiveMaxDegrees: number | null;
|
|
70
|
+
negativeScale: BilateralAxisDirectionScaleState;
|
|
71
|
+
positiveScale: BilateralAxisDirectionScaleState;
|
|
72
|
+
}
|
|
73
|
+
export interface BilateralAxisBindingState {
|
|
74
|
+
axis: BoneAxisKey;
|
|
75
|
+
familyLabel: string;
|
|
76
|
+
leftBoneName: string;
|
|
77
|
+
leftNodeKey: string;
|
|
78
|
+
rightBoneName: string;
|
|
79
|
+
rightNodeKey: string;
|
|
80
|
+
selectedSide: 'left' | 'right';
|
|
81
|
+
shared: BilateralAxisScopeBindingState;
|
|
82
|
+
left: BilateralAxisScopeBindingState;
|
|
83
|
+
right: BilateralAxisScopeBindingState;
|
|
84
|
+
}
|
|
85
|
+
export declare const DEFAULT_AXIS_TO_CHANNEL: Record<BoneAxisKey, RotationChannel>;
|
|
86
|
+
export declare const DEFAULT_BONE_MAX_DEGREES = 60;
|
|
87
|
+
export declare function inferChiralBoneNamePair(boneNames: string[], selectedBoneName: string): ChiralBoneNamePair | null;
|
|
88
|
+
export declare function formatAxisLabel(axis: BoneAxisKey): string;
|
|
89
|
+
export declare function formatAxisDirectionLabel(axis: BoneAxisKey, direction: BoneAxisDirection): string;
|
|
90
|
+
export declare function stripConfiguredBoneAffixes(profile: {
|
|
91
|
+
bonePrefix?: string;
|
|
92
|
+
boneSuffix?: string;
|
|
93
|
+
} | null | undefined, boneName: string): string;
|
|
94
|
+
export declare function findNodeKeyForBone(boneName: string, profile: Profile | null): string | null;
|
|
95
|
+
export declare function ensureBoneNodeKey(profile: Profile, boneName: string): {
|
|
96
|
+
nodeKey: string;
|
|
97
|
+
boneNodes: Record<string, string>;
|
|
98
|
+
};
|
|
99
|
+
export declare function ensureBilateralBoneNodeKeys(profile: Profile, leftBoneName: string, rightBoneName: string): Profile;
|
|
100
|
+
export declare function resolveBoneNameForNodeKey(profile: Profile, nodeKey: string): string | null;
|
|
101
|
+
export declare function inferEyeControlScope(profile: Pick<Profile, 'auToBones'>, binding: Pick<BoneAxisBindingState, 'nodeKey' | 'negativeAuId' | 'positiveAuId'>): BoneControlScope | null;
|
|
102
|
+
export declare function inferEyeControlFamily(binding: Pick<BoneAxisBindingState, 'nodeKey' | 'axis'>): BoneControlFamily | null;
|
|
103
|
+
export declare function resolveBoneAxisChannel(profile: Profile, boneName: string, axis: BoneAxisKey): RotationChannel;
|
|
104
|
+
export declare function getBoneAxisBindingState(profile: Profile, boneName: string, axis: BoneAxisKey): BoneAxisBindingState;
|
|
105
|
+
export declare function getBilateralAxisBindingState(profile: Profile, boneName: string, axis: BoneAxisKey): BilateralAxisBindingState | null;
|
|
106
|
+
export declare function getAUBoneBindingState(profile: Profile, auId: number): AUBoneBindingState;
|
|
107
|
+
export declare function applyAUBoneBindingUpdate(profile: Profile, auId: number, update: {
|
|
108
|
+
boneName: string | null;
|
|
109
|
+
axis: BoneAxisKey;
|
|
110
|
+
direction: BoneAxisDirection;
|
|
111
|
+
maxDegrees: number | null;
|
|
112
|
+
}): Profile;
|
|
113
|
+
export declare function resolveContinuumDisplayLabel(profile: Pick<Profile, 'continuumPairs' | 'continuumLabels' | 'auInfo' | 'boneNodes' | 'bonePrefix' | 'boneSuffix'>, negativeAuId: number, positiveAuId: number): string;
|
|
114
|
+
export declare function applyBoneAxisBindingUpdate(profile: Profile, boneName: string, axis: BoneAxisKey, update: BoneAxisBindingUpdate): Profile;
|
|
115
|
+
export declare function applyBilateralAxisBindingUpdate(profile: Profile, boneName: string, axis: BoneAxisKey, scope: BilateralBoneAxisScope, update: BoneAxisBindingUpdate): Profile;
|
|
116
|
+
export declare function isJointControlAuInfo(info: Pick<AUInfo, 'facePart'> | null | undefined): boolean;
|
|
117
|
+
export declare function classifyAuAsJointControl(profile: Profile, auId: number, options?: {
|
|
118
|
+
boneName?: string | null;
|
|
119
|
+
nodeKey?: string | null;
|
|
120
|
+
}): Profile;
|
|
121
|
+
export declare function createBoneAxisAu(profile: Profile, boneName: string, axis: BoneAxisKey, direction: BoneAxisDirection, options?: {
|
|
122
|
+
name?: string;
|
|
123
|
+
}): CreatedBoneAxisAu;
|
|
124
|
+
export declare function createBilateralBoneAxisAu(profile: Profile, boneName: string, axis: BoneAxisKey, scope: BilateralBoneAxisScope, direction: BoneAxisDirection, options?: {
|
|
125
|
+
name?: string;
|
|
126
|
+
}): CreatedBoneAxisAu;
|
|
127
|
+
export declare function buildBoneAuOptions(profile: Profile): Array<{
|
|
128
|
+
value: string;
|
|
129
|
+
label: string;
|
|
130
|
+
detail?: string;
|
|
131
|
+
}>;
|
|
132
|
+
export declare function getAxisFromChannel(channel: string | undefined): BoneAxisKey | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import type { BoneResolutionProfile } from '../regions/regionMapping';
|
|
3
|
+
import type { Region } from '../regions/types';
|
|
4
|
+
type SemanticSide = 'left' | 'right';
|
|
5
|
+
export interface AnnotationLaterality {
|
|
6
|
+
leftSideX: 1 | -1;
|
|
7
|
+
confidence: number;
|
|
8
|
+
evidence: string[];
|
|
9
|
+
}
|
|
10
|
+
type LateralityRegion = Pick<Region, 'name' | 'cameraAngle' | 'parent'>;
|
|
11
|
+
export declare function getDefaultAnnotationLaterality(): AnnotationLaterality;
|
|
12
|
+
export declare function normalizeCameraAngle(angle: number): number;
|
|
13
|
+
export declare function getRegionSemanticSide(regionName?: string): SemanticSide | null;
|
|
14
|
+
export declare function getSemanticHorizontalSign(regionName: string, laterality: AnnotationLaterality): 1 | -1 | null;
|
|
15
|
+
export declare function getSemanticHorizontalSignForSide(side: SemanticSide, laterality: AnnotationLaterality): 1 | -1;
|
|
16
|
+
export declare function detectAnnotationLaterality(model: THREE.Object3D | null, regions: Region[], characterConfig: BoneResolutionProfile | null): AnnotationLaterality;
|
|
17
|
+
export declare function resolveRegionCameraAngle(region: LateralityRegion, laterality: AnnotationLaterality): number | undefined;
|
|
18
|
+
export declare function resolveRegionVisibilityCameraAngle(region: LateralityRegion, laterality: AnnotationLaterality): number | undefined;
|
|
19
|
+
export declare function toWorldDirection(model: THREE.Object3D | null, localDirection: THREE.Vector3): THREE.Vector3;
|
|
20
|
+
export declare function toModelLocalDirection(model: THREE.Object3D | null, worldDirection: THREE.Vector3): THREE.Vector3;
|
|
21
|
+
export declare function getWorldDirectionForCameraAngle(model: THREE.Object3D | null, cameraAngle: number): THREE.Vector3;
|
|
22
|
+
export declare function getModelLocalOrbitAngle(model: THREE.Object3D | null, modelCenter: THREE.Vector3, worldPosition: THREE.Vector3): number;
|
|
23
|
+
export declare function passesMarkerCameraAngleGate(params: {
|
|
24
|
+
markerAngle?: number;
|
|
25
|
+
currentCameraAngle?: number;
|
|
26
|
+
rangeDegrees?: number;
|
|
27
|
+
}): boolean;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
export type CameraRelativeGazeOffset = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
};
|
|
6
|
+
export interface CameraRelativeGazeOptions {
|
|
7
|
+
yawWeight?: number;
|
|
8
|
+
pitchWeight?: number;
|
|
9
|
+
epsilon?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Convert the current camera position into a normalized gaze offset in model-local space.
|
|
13
|
+
* This is intentionally pure so apps can cache or subscribe to camera changes however they want.
|
|
14
|
+
*/
|
|
15
|
+
export declare function computeCameraRelativeGazeOffset(model: THREE.Object3D | null, cameraPosition: THREE.Vector3, targetPosition: THREE.Vector3, options?: CameraRelativeGazeOptions): CameraRelativeGazeOffset;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { WasmCameraFlightHandle, WasmCameraOrbitHandle } from '../wasmTypes';
|
|
2
|
+
export interface Vec3Like {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
z: number;
|
|
6
|
+
}
|
|
7
|
+
export interface QuatLike {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
z: number;
|
|
11
|
+
w: number;
|
|
12
|
+
}
|
|
13
|
+
export interface BoundsLike {
|
|
14
|
+
center: Vec3Like;
|
|
15
|
+
size: Vec3Like;
|
|
16
|
+
}
|
|
17
|
+
export interface CameraFramingResult {
|
|
18
|
+
position: Vec3Like;
|
|
19
|
+
target: Vec3Like;
|
|
20
|
+
distance: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CameraPoseSample extends Omit<CameraFramingResult, 'distance'> {
|
|
23
|
+
done: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface FocusFramingOptions {
|
|
26
|
+
focusBounds: BoundsLike;
|
|
27
|
+
modelBounds?: BoundsLike | null;
|
|
28
|
+
modelQuaternion?: QuatLike | null;
|
|
29
|
+
fovDegrees: number;
|
|
30
|
+
aspect: number;
|
|
31
|
+
minDistance: number;
|
|
32
|
+
closeUpPaddingFactor: number;
|
|
33
|
+
zoomPaddingFactor: number;
|
|
34
|
+
fullBodyPaddingFactor: number;
|
|
35
|
+
overridePadding?: number;
|
|
36
|
+
cameraAngle?: number;
|
|
37
|
+
angleSpace?: 'model' | 'world';
|
|
38
|
+
}
|
|
39
|
+
export interface FullBodyFramingOptions {
|
|
40
|
+
boxMin: Vec3Like;
|
|
41
|
+
boxMax: Vec3Like;
|
|
42
|
+
modelQuaternion?: QuatLike | null;
|
|
43
|
+
fovDegrees: number;
|
|
44
|
+
aspect: number;
|
|
45
|
+
minDistance: number;
|
|
46
|
+
fullBodyPaddingFactor: number;
|
|
47
|
+
overridePadding?: number;
|
|
48
|
+
cameraAngle?: number;
|
|
49
|
+
angleSpace?: 'model' | 'world';
|
|
50
|
+
}
|
|
51
|
+
export type MarkerSoloState = 'none' | 'soloed' | 'other-soloed';
|
|
52
|
+
export interface MarkerVisibilityAnimationFactors {
|
|
53
|
+
itemOpacityFactor: number;
|
|
54
|
+
labelScaleFactor: number;
|
|
55
|
+
lineOpacityFactor: number;
|
|
56
|
+
}
|
|
57
|
+
export interface MarkerEndpointSeparationOptions {
|
|
58
|
+
/** Packed marker surface anchors as `[x, y, z, ...]`. */
|
|
59
|
+
starts: ArrayLike<number>;
|
|
60
|
+
/** Packed marker leader-line ends as `[x, y, z, ...]`. */
|
|
61
|
+
ends: ArrayLike<number>;
|
|
62
|
+
modelCenter: Vec3Like;
|
|
63
|
+
modelHeight: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class RustCameraFlight {
|
|
66
|
+
private handle;
|
|
67
|
+
constructor(handle: WasmCameraFlightHandle);
|
|
68
|
+
sample(elapsedMs: number): CameraPoseSample;
|
|
69
|
+
get durationMs(): number;
|
|
70
|
+
dispose(): void;
|
|
71
|
+
}
|
|
72
|
+
export declare class RustCameraOrbit {
|
|
73
|
+
private handle;
|
|
74
|
+
constructor(handle: WasmCameraOrbitHandle);
|
|
75
|
+
sample(elapsedMs: number): CameraPoseSample;
|
|
76
|
+
get durationMs(): number;
|
|
77
|
+
dispose(): void;
|
|
78
|
+
}
|
|
79
|
+
export declare class RustAnnotationCameraCore {
|
|
80
|
+
private core;
|
|
81
|
+
private constructor();
|
|
82
|
+
static create(): Promise<RustAnnotationCameraCore>;
|
|
83
|
+
normalizeCameraAngle(angle: number): number;
|
|
84
|
+
worldDirectionForCameraAngle(modelQuaternion: QuatLike | null, cameraAngle: number): Vec3Like;
|
|
85
|
+
resolveFocusCameraDirection(modelQuaternion: QuatLike | null, effectiveAngle: number, cameraAngle: number | undefined, angleSpace?: 'model' | 'world'): Vec3Like;
|
|
86
|
+
resolveAutoCloseupAngle(horizontalOffset: number, focusSize: Vec3Like, modelSize: Vec3Like): number | undefined;
|
|
87
|
+
focusPaddingFactor(size: number, closeUpPaddingFactor: number, zoomPaddingFactor: number, fullBodyPaddingFactor: number): number;
|
|
88
|
+
solveFocusFraming(options: FocusFramingOptions): CameraFramingResult;
|
|
89
|
+
solveFullBodyFraming(options: FullBodyFramingOptions): CameraFramingResult;
|
|
90
|
+
createCameraFlight(startPosition: Vec3Like, startTarget: Vec3Like, endPosition: Vec3Like, endTarget: Vec3Like, durationMs: number): RustCameraFlight;
|
|
91
|
+
createCameraOrbit(center: Vec3Like, radius: number, height: number, durationMs: number): RustCameraOrbit;
|
|
92
|
+
passesMarkerCameraAngleGate(params: {
|
|
93
|
+
markerAngle?: number;
|
|
94
|
+
currentCameraAngle?: number;
|
|
95
|
+
rangeDegrees?: number;
|
|
96
|
+
}): boolean;
|
|
97
|
+
shouldShowMarker(params: {
|
|
98
|
+
hiddenChild: boolean;
|
|
99
|
+
solo: MarkerSoloState;
|
|
100
|
+
markerAngle?: number;
|
|
101
|
+
currentCameraAngle?: number;
|
|
102
|
+
}): boolean;
|
|
103
|
+
resolveViewportConstrainedLineScale(params: {
|
|
104
|
+
startClip: readonly number[];
|
|
105
|
+
endClip: readonly number[];
|
|
106
|
+
safeX: number;
|
|
107
|
+
safeY: number;
|
|
108
|
+
minLengthRatio?: number;
|
|
109
|
+
}): {
|
|
110
|
+
visible: boolean;
|
|
111
|
+
lineScale: number;
|
|
112
|
+
};
|
|
113
|
+
resolveViewportSafeBounds(params: {
|
|
114
|
+
labelScaleX: number;
|
|
115
|
+
labelScaleY: number;
|
|
116
|
+
projectionXScale: number;
|
|
117
|
+
projectionYScale: number;
|
|
118
|
+
viewportWidth: number;
|
|
119
|
+
viewportHeight: number;
|
|
120
|
+
edgePaddingPx?: number;
|
|
121
|
+
}): {
|
|
122
|
+
safeX: number;
|
|
123
|
+
safeY: number;
|
|
124
|
+
};
|
|
125
|
+
/** Returns `(segments + 1)` points as a flat `[x, y, z, ...]` array. */
|
|
126
|
+
sampleMarkerBezierCurve(start: Vec3Like, end: Vec3Like, segments?: number): Float32Array;
|
|
127
|
+
/** Returns `(segments + 1)` points as a flat `[x, y, z, ...]` array. */
|
|
128
|
+
sampleMarkerArcCurve(start: Vec3Like, end: Vec3Like, segments?: number): Float32Array;
|
|
129
|
+
markerVisibilityAnimationFactors(visible: boolean, t: number): MarkerVisibilityAnimationFactors;
|
|
130
|
+
/**
|
|
131
|
+
* Separates overlapping leader-line endpoints while retaining marker anchors
|
|
132
|
+
* and leader-line lengths. Returns packed `[x, y, z, ...]` endpoints.
|
|
133
|
+
*/
|
|
134
|
+
separateOverlappingMarkerEndpoints(options: MarkerEndpointSeparationOptions): Float32Array;
|
|
135
|
+
}
|
|
136
|
+
export declare function createRustAnnotationCameraCore(): Promise<RustAnnotationCameraCore>;
|
package/dist/cljs.cjs
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/cljs.ts
|
|
4
|
+
var LOOP_MODES = /* @__PURE__ */ new Set(["once", "repeat", "pingpong"]);
|
|
5
|
+
function nowMs() {
|
|
6
|
+
return Date.now();
|
|
7
|
+
}
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function finiteNumber(value) {
|
|
12
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
13
|
+
}
|
|
14
|
+
function numberOr(value, fallback) {
|
|
15
|
+
return finiteNumber(value) ? value : fallback;
|
|
16
|
+
}
|
|
17
|
+
function clamp(low, high, value) {
|
|
18
|
+
const next = numberOr(value, low);
|
|
19
|
+
return Math.min(high, Math.max(low, next));
|
|
20
|
+
}
|
|
21
|
+
function keyToString(value) {
|
|
22
|
+
if (typeof value === "string") return value;
|
|
23
|
+
if (typeof value === "number") return String(value);
|
|
24
|
+
if (typeof value === "symbol") return value.description ?? "";
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
function getConnectorFunction(connector, name) {
|
|
28
|
+
const candidate = connector[name];
|
|
29
|
+
return typeof candidate === "function" ? candidate : null;
|
|
30
|
+
}
|
|
31
|
+
function callFirst(connector, names, ...args) {
|
|
32
|
+
for (const name of names) {
|
|
33
|
+
const callback = getConnectorFunction(connector, name);
|
|
34
|
+
if (callback) {
|
|
35
|
+
return callback(...args);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
function normalizePoint(point) {
|
|
41
|
+
const source = isRecord(point) ? point : {};
|
|
42
|
+
return {
|
|
43
|
+
time: numberOr(source.time ?? source.t, 0),
|
|
44
|
+
intensity: numberOr(source.intensity ?? source.v, 0),
|
|
45
|
+
inherit: Boolean(source.inherit)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function normalizeCurves(curves) {
|
|
49
|
+
if (!isRecord(curves)) {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
Object.entries(curves).map(([curveId, points]) => [
|
|
54
|
+
keyToString(curveId),
|
|
55
|
+
(Array.isArray(points) ? points : []).map(normalizePoint).sort((a, b) => a.time - b.time)
|
|
56
|
+
])
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function calculateDuration(curves, fallback) {
|
|
60
|
+
return Object.values(curves).reduce((duration, points) => {
|
|
61
|
+
const last = points[points.length - 1];
|
|
62
|
+
return Math.max(duration, numberOr(last?.time, duration));
|
|
63
|
+
}, numberOr(fallback, 0));
|
|
64
|
+
}
|
|
65
|
+
function normalizeLoopMode(options) {
|
|
66
|
+
const mode = options.loopMode ?? options.mixerLoopMode;
|
|
67
|
+
if (typeof mode === "string" && LOOP_MODES.has(mode)) {
|
|
68
|
+
return mode;
|
|
69
|
+
}
|
|
70
|
+
return options.loop === false ? "once" : "repeat";
|
|
71
|
+
}
|
|
72
|
+
function normalizeOptions(options) {
|
|
73
|
+
const source = isRecord(options) ? options : {};
|
|
74
|
+
const loopMode = normalizeLoopMode(source);
|
|
75
|
+
const rate = source.playbackRate ?? source.rate ?? source.speed;
|
|
76
|
+
const weight = source.weight ?? source.intensity ?? source.mixerWeight;
|
|
77
|
+
return {
|
|
78
|
+
...source,
|
|
79
|
+
weight: Math.max(0, numberOr(weight, 1)),
|
|
80
|
+
rate: Math.max(0, numberOr(rate, 1)),
|
|
81
|
+
loopMode,
|
|
82
|
+
loop: loopMode !== "once",
|
|
83
|
+
repeatCount: finiteNumber(source.repeatCount) ? source.repeatCount : void 0,
|
|
84
|
+
reverse: Boolean(source.reverse),
|
|
85
|
+
source: keyToString(source.source) || "clip",
|
|
86
|
+
startTime: Math.max(0, numberOr(source.startTime, 0)),
|
|
87
|
+
balance: clamp(-1, 1, source.balance),
|
|
88
|
+
jawScale: Math.max(0, numberOr(source.jawScale, 1)),
|
|
89
|
+
intensityScale: Math.max(0, numberOr(source.intensityScale, 1))
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function makeActionId(clipName) {
|
|
93
|
+
return `${clipName}#${nowMs()}-${Math.floor(Math.random() * 1e6)}`;
|
|
94
|
+
}
|
|
95
|
+
function emitCommand(connector, op, clipName, payload = {}) {
|
|
96
|
+
const command = {
|
|
97
|
+
type: "clipCommand",
|
|
98
|
+
op,
|
|
99
|
+
clipName,
|
|
100
|
+
timestamp: nowMs(),
|
|
101
|
+
...payload
|
|
102
|
+
};
|
|
103
|
+
connector.onCommand?.(command);
|
|
104
|
+
return command;
|
|
105
|
+
}
|
|
106
|
+
function emitState(connector, state) {
|
|
107
|
+
connector.onState?.(snapshotState(state));
|
|
108
|
+
}
|
|
109
|
+
function snapshotState(state) {
|
|
110
|
+
return {
|
|
111
|
+
handles: Object.fromEntries(
|
|
112
|
+
Object.entries(state.handles).map(([clipName, handle]) => [clipName, { ...handle }])
|
|
113
|
+
),
|
|
114
|
+
order: [...state.order],
|
|
115
|
+
eventCount: state.eventCount,
|
|
116
|
+
lastEvent: state.lastEvent ? { ...state.lastEvent } : null,
|
|
117
|
+
lastUpdatedTime: state.lastUpdatedTime,
|
|
118
|
+
config: { ...state.config }
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function createAnimationRuntime(config = null, connector = null) {
|
|
122
|
+
const renderer = connector ?? {};
|
|
123
|
+
const state = {
|
|
124
|
+
handles: {},
|
|
125
|
+
order: [],
|
|
126
|
+
eventCount: 0,
|
|
127
|
+
lastEvent: null,
|
|
128
|
+
lastUpdatedTime: null,
|
|
129
|
+
config: isRecord(config) ? { ...config } : {}
|
|
130
|
+
};
|
|
131
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
132
|
+
const finishers = /* @__PURE__ */ new Map();
|
|
133
|
+
const upsertHandle = (handle) => {
|
|
134
|
+
state.handles[handle.clipName] = handle;
|
|
135
|
+
if (!state.order.includes(handle.clipName)) {
|
|
136
|
+
state.order.push(handle.clipName);
|
|
137
|
+
}
|
|
138
|
+
state.lastUpdatedTime = nowMs();
|
|
139
|
+
};
|
|
140
|
+
const updateHandle = (clipName, apply) => {
|
|
141
|
+
const current = state.handles[clipName];
|
|
142
|
+
if (!current) return;
|
|
143
|
+
state.handles[clipName] = apply({ ...current, updatedAt: nowMs() });
|
|
144
|
+
state.lastUpdatedTime = nowMs();
|
|
145
|
+
};
|
|
146
|
+
const removeHandle = (clipName) => {
|
|
147
|
+
delete state.handles[clipName];
|
|
148
|
+
state.order = state.order.filter((name) => name !== clipName);
|
|
149
|
+
state.lastUpdatedTime = nowMs();
|
|
150
|
+
};
|
|
151
|
+
const notifyListeners = (clipName, event) => {
|
|
152
|
+
for (const listener of Array.from(listeners.get(clipName) ?? [])) {
|
|
153
|
+
try {
|
|
154
|
+
listener(event);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error("[Embody runtime] clip listener failed", error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const resolveFinished = (clipName) => {
|
|
161
|
+
const resolve = finishers.get(clipName);
|
|
162
|
+
if (resolve) {
|
|
163
|
+
resolve();
|
|
164
|
+
finishers.delete(clipName);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const makeHandle = (clipName) => {
|
|
168
|
+
const finished = new Promise((resolve) => {
|
|
169
|
+
finishers.set(clipName, resolve);
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
clipName,
|
|
173
|
+
actionId: state.handles[clipName]?.actionId,
|
|
174
|
+
play: () => {
|
|
175
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "playing" }));
|
|
176
|
+
emitCommand(renderer, "play", clipName);
|
|
177
|
+
callFirst(renderer, ["playClip", "play"], clipName);
|
|
178
|
+
emitState(renderer, state);
|
|
179
|
+
},
|
|
180
|
+
stop: () => {
|
|
181
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "stopped" }));
|
|
182
|
+
emitCommand(renderer, "stop", clipName);
|
|
183
|
+
callFirst(renderer, ["stopClip", "stop"], clipName);
|
|
184
|
+
resolveFinished(clipName);
|
|
185
|
+
emitState(renderer, state);
|
|
186
|
+
},
|
|
187
|
+
pause: () => {
|
|
188
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "paused" }));
|
|
189
|
+
emitCommand(renderer, "pause", clipName);
|
|
190
|
+
callFirst(renderer, ["pauseClip", "pause"], clipName);
|
|
191
|
+
emitState(renderer, state);
|
|
192
|
+
},
|
|
193
|
+
resume: () => {
|
|
194
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "playing" }));
|
|
195
|
+
emitCommand(renderer, "resume", clipName);
|
|
196
|
+
callFirst(renderer, ["resumeClip", "resume"], clipName);
|
|
197
|
+
emitState(renderer, state);
|
|
198
|
+
},
|
|
199
|
+
setWeight: (weight) => {
|
|
200
|
+
const nextWeight = Math.max(0, numberOr(weight, 1));
|
|
201
|
+
updateHandle(clipName, (handle) => ({ ...handle, weight: nextWeight }));
|
|
202
|
+
emitCommand(renderer, "setWeight", clipName, { weight: nextWeight });
|
|
203
|
+
callFirst(renderer, ["setClipWeight", "setWeight"], clipName, nextWeight);
|
|
204
|
+
emitState(renderer, state);
|
|
205
|
+
},
|
|
206
|
+
setPlaybackRate: (rate) => {
|
|
207
|
+
const nextRate = Math.max(0, numberOr(rate, 1));
|
|
208
|
+
updateHandle(clipName, (handle) => ({ ...handle, rate: nextRate }));
|
|
209
|
+
emitCommand(renderer, "setPlaybackRate", clipName, { rate: nextRate });
|
|
210
|
+
callFirst(renderer, ["setClipPlaybackRate", "setPlaybackRate"], clipName, nextRate);
|
|
211
|
+
emitState(renderer, state);
|
|
212
|
+
},
|
|
213
|
+
setLoop: (mode, repeatCount) => {
|
|
214
|
+
const nextMode = LOOP_MODES.has(mode) ? mode : "repeat";
|
|
215
|
+
updateHandle(clipName, (handle) => ({
|
|
216
|
+
...handle,
|
|
217
|
+
loopMode: nextMode,
|
|
218
|
+
loop: nextMode !== "once",
|
|
219
|
+
repeatCount
|
|
220
|
+
}));
|
|
221
|
+
emitCommand(renderer, "setLoop", clipName, { loopMode: nextMode, repeatCount });
|
|
222
|
+
callFirst(renderer, ["setClipLoop", "setLoop"], clipName, nextMode, repeatCount);
|
|
223
|
+
emitState(renderer, state);
|
|
224
|
+
},
|
|
225
|
+
setTime: (time) => {
|
|
226
|
+
const duration = state.handles[clipName]?.duration ?? 0;
|
|
227
|
+
const nextTime = clamp(0, duration, time);
|
|
228
|
+
updateHandle(clipName, (handle) => ({ ...handle, time: nextTime }));
|
|
229
|
+
emitCommand(renderer, "setTime", clipName, { time: nextTime });
|
|
230
|
+
callFirst(renderer, ["setClipTime", "setTime"], clipName, nextTime);
|
|
231
|
+
emitState(renderer, state);
|
|
232
|
+
},
|
|
233
|
+
getTime: () => {
|
|
234
|
+
const rendererTime = callFirst(renderer, ["getClipTime", "getTime"], clipName);
|
|
235
|
+
return numberOr(rendererTime, state.handles[clipName]?.time ?? 0);
|
|
236
|
+
},
|
|
237
|
+
getDuration: () => {
|
|
238
|
+
const rendererDuration = callFirst(renderer, ["getClipDuration", "getDuration"], clipName);
|
|
239
|
+
return numberOr(rendererDuration, state.handles[clipName]?.duration ?? 0);
|
|
240
|
+
},
|
|
241
|
+
subscribe: (listener) => {
|
|
242
|
+
const clipListeners = listeners.get(clipName) ?? /* @__PURE__ */ new Set();
|
|
243
|
+
clipListeners.add(listener);
|
|
244
|
+
listeners.set(clipName, clipListeners);
|
|
245
|
+
return () => {
|
|
246
|
+
clipListeners.delete(listener);
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
finished
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
const acceptClipEvent = (event) => {
|
|
253
|
+
const eventRecord = isRecord(event) ? event : {};
|
|
254
|
+
const clipName = keyToString(eventRecord.clipName ?? eventRecord.name);
|
|
255
|
+
if (!clipName) return true;
|
|
256
|
+
const eventType = keyToString(eventRecord.type ?? eventRecord.event);
|
|
257
|
+
const currentTime = numberOr(
|
|
258
|
+
eventRecord.currentTime,
|
|
259
|
+
numberOr(eventRecord.time, state.handles[clipName]?.time ?? 0)
|
|
260
|
+
);
|
|
261
|
+
if (["time", "progress", "frame", "keyframe", "seek", "loop"].includes(eventType)) {
|
|
262
|
+
updateHandle(clipName, (handle) => ({ ...handle, time: currentTime }));
|
|
263
|
+
} else if (eventType === "paused") {
|
|
264
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "paused" }));
|
|
265
|
+
} else if (["resumed", "started", "played"].includes(eventType)) {
|
|
266
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "playing" }));
|
|
267
|
+
} else if (["completed", "finished"].includes(eventType)) {
|
|
268
|
+
updateHandle(clipName, (handle) => ({
|
|
269
|
+
...handle,
|
|
270
|
+
status: "completed",
|
|
271
|
+
time: handle.duration
|
|
272
|
+
}));
|
|
273
|
+
resolveFinished(clipName);
|
|
274
|
+
} else if (["stopped", "removed"].includes(eventType)) {
|
|
275
|
+
updateHandle(clipName, (handle) => ({ ...handle, status: "stopped" }));
|
|
276
|
+
resolveFinished(clipName);
|
|
277
|
+
}
|
|
278
|
+
state.eventCount += 1;
|
|
279
|
+
state.lastEvent = { ...eventRecord, timestamp: nowMs() };
|
|
280
|
+
notifyListeners(clipName, event);
|
|
281
|
+
emitState(renderer, state);
|
|
282
|
+
return true;
|
|
283
|
+
};
|
|
284
|
+
const buildClip = (clipNameInput, curvesInput, optionsInput) => {
|
|
285
|
+
const clipName = keyToString(clipNameInput);
|
|
286
|
+
if (!clipName) return null;
|
|
287
|
+
const curves = normalizeCurves(curvesInput);
|
|
288
|
+
const options = normalizeOptions(optionsInput);
|
|
289
|
+
const connectorResult = callFirst(renderer, ["buildClip", "playClip"], clipName, curves, options);
|
|
290
|
+
if (connectorResult == null || connectorResult === false) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
const result = isRecord(connectorResult) ? connectorResult : {};
|
|
294
|
+
const duration = calculateDuration(curves, result.duration);
|
|
295
|
+
const handleState = {
|
|
296
|
+
clipName,
|
|
297
|
+
actionId: keyToString(result.actionId ?? result.id) || makeActionId(clipName),
|
|
298
|
+
duration,
|
|
299
|
+
time: numberOr(result.time, options.startTime),
|
|
300
|
+
weight: options.weight,
|
|
301
|
+
rate: options.rate,
|
|
302
|
+
loopMode: options.loopMode,
|
|
303
|
+
loop: options.loop,
|
|
304
|
+
repeatCount: options.repeatCount,
|
|
305
|
+
reverse: options.reverse,
|
|
306
|
+
source: options.source,
|
|
307
|
+
status: "playing",
|
|
308
|
+
createdAt: nowMs(),
|
|
309
|
+
updatedAt: nowMs()
|
|
310
|
+
};
|
|
311
|
+
upsertHandle(handleState);
|
|
312
|
+
emitCommand(renderer, "buildClip", clipName, {
|
|
313
|
+
actionId: handleState.actionId,
|
|
314
|
+
duration: handleState.duration,
|
|
315
|
+
options
|
|
316
|
+
});
|
|
317
|
+
emitState(renderer, state);
|
|
318
|
+
return makeHandle(clipName);
|
|
319
|
+
};
|
|
320
|
+
return {
|
|
321
|
+
snapshot: () => snapshotState(state),
|
|
322
|
+
buildClip,
|
|
323
|
+
playSnippet: (clipName, curves, options) => buildClip(clipName, curves, { ...options, source: options?.source ?? "snippet" }),
|
|
324
|
+
acceptClipEvent,
|
|
325
|
+
cleanupSnippet: (clipNameInput) => {
|
|
326
|
+
const clipName = keyToString(clipNameInput);
|
|
327
|
+
if (!clipName) return false;
|
|
328
|
+
removeHandle(clipName);
|
|
329
|
+
emitCommand(renderer, "cleanupSnippet", clipName);
|
|
330
|
+
callFirst(renderer, ["cleanupSnippet", "removeClip"], clipName);
|
|
331
|
+
emitState(renderer, state);
|
|
332
|
+
return true;
|
|
333
|
+
},
|
|
334
|
+
updateClipParams: (clipNameInput, params) => {
|
|
335
|
+
const clipName = keyToString(clipNameInput);
|
|
336
|
+
if (!clipName) return false;
|
|
337
|
+
const source = isRecord(params) ? params : {};
|
|
338
|
+
updateHandle(clipName, (handle) => ({
|
|
339
|
+
...handle,
|
|
340
|
+
weight: finiteNumber(source.weight) ? Math.max(0, source.weight) : handle.weight,
|
|
341
|
+
rate: finiteNumber(source.rate) ? Math.max(0, source.rate) : handle.rate,
|
|
342
|
+
loopMode: typeof source.loopMode === "string" && LOOP_MODES.has(source.loopMode) ? source.loopMode : handle.loopMode,
|
|
343
|
+
loop: typeof source.loop === "boolean" ? source.loop : handle.loop,
|
|
344
|
+
repeatCount: finiteNumber(source.repeatCount) ? source.repeatCount : handle.repeatCount,
|
|
345
|
+
reverse: typeof source.reverse === "boolean" ? source.reverse : handle.reverse
|
|
346
|
+
}));
|
|
347
|
+
emitCommand(renderer, "updateClipParams", clipName, { params: source });
|
|
348
|
+
const result = callFirst(renderer, ["updateClipParams"], clipName, source);
|
|
349
|
+
emitState(renderer, state);
|
|
350
|
+
return result === false ? false : true;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function installEmbody(target = globalThis) {
|
|
355
|
+
target.createAnimationRuntime = createAnimationRuntime;
|
|
356
|
+
return { createAnimationRuntime };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
exports.createAnimationRuntime = createAnimationRuntime;
|
|
360
|
+
exports.installEmbody = installEmbody;
|
|
361
|
+
//# sourceMappingURL=cljs.cjs.map
|
|
362
|
+
//# sourceMappingURL=cljs.cjs.map
|