@vincentt-xr/sdk 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GROUNDING.md +537 -0
- package/LICENSE +7 -0
- package/README.md +85 -0
- package/dist/DebuggerUI-CEfdddkZ.cjs +1 -0
- package/dist/DebuggerUI-Dgop6Q6o.js +269 -0
- package/dist/audio/AudioEngine.d.ts +98 -0
- package/dist/audio/index.d.ts +34 -0
- package/dist/components/AspectRatioContainer/index.d.ts +10 -0
- package/dist/components/BodyTracker/index.d.ts +15 -0
- package/dist/components/Debugger/DebuggerProvider.d.ts +22 -0
- package/dist/components/Debugger/DebuggerUI.d.ts +13 -0
- package/dist/components/Debugger/index.d.ts +38 -0
- package/dist/components/Debugger/styles.d.ts +19 -0
- package/dist/components/FaceMesh/FaceMesh.d.ts +26 -0
- package/dist/components/FaceMesh/canonical/CanonicalFaceMeshRenderer.d.ts +34 -0
- package/dist/components/FaceMesh/canonical/parseCanonicalFaceModel.d.ts +16 -0
- package/dist/components/FaceMesh/canonical/useCanonicalFaceMesh.d.ts +15 -0
- package/dist/components/FaceMesh/deform.d.ts +36 -0
- package/dist/components/FaceMesh/index.d.ts +6 -0
- package/dist/components/FaceMesh/indices.d.ts +1 -0
- package/dist/components/FaceMesh/material/blendModes.d.ts +2 -0
- package/dist/components/FaceMesh/material/defaults.d.ts +3 -0
- package/dist/components/FaceMesh/material/index.d.ts +5 -0
- package/dist/components/FaceMesh/material/options.d.ts +25 -0
- package/dist/components/FaceMesh/material/support.d.ts +3 -0
- package/dist/components/FaceMesh/material/types.d.ts +23 -0
- package/dist/components/FaceMesh/uv.d.ts +1 -0
- package/dist/components/FaceRetouch/Background.d.ts +16 -0
- package/dist/components/FaceRetouch/index.d.ts +6 -0
- package/dist/components/FaceRetouch/landmarkMask.d.ts +23 -0
- package/dist/components/FaceRetouch/retouchRegions.d.ts +23 -0
- package/dist/components/FaceRetouch/useFaceRetouchMask.d.ts +14 -0
- package/dist/components/FaceRetouch/useFaceRetouchRegions.d.ts +12 -0
- package/dist/components/FaceTracker/BoundingBoxOverlay.d.ts +20 -0
- package/dist/components/FaceTracker/index.d.ts +54 -0
- package/dist/components/GestureTracker/index.d.ts +31 -0
- package/dist/components/GestureTrigger/index.d.ts +3 -0
- package/dist/components/HandTracker/index.d.ts +24 -0
- package/dist/components/HeadBinding/index.d.ts +31 -0
- package/dist/components/HeadBindingOccluder/index.d.ts +36 -0
- package/dist/components/HeadOccluder/index.d.ts +13 -0
- package/dist/components/Panel/index.d.ts +71 -0
- package/dist/components/PerspectiveCamera/index.d.ts +14 -0
- package/dist/components/ScreenSpaceUI/ScreenTransform.d.ts +54 -0
- package/dist/components/ScreenSpaceUI/context.d.ts +29 -0
- package/dist/components/ScreenSpaceUI/index.d.ts +40 -0
- package/dist/components/Segmentation/index.d.ts +44 -0
- package/dist/components/SpriteAnimation/index.d.ts +52 -0
- package/dist/components/TextLabel/index.d.ts +62 -0
- package/dist/components/TrackingAnchor/index.d.ts +74 -0
- package/dist/components/VideoBackground/index.d.ts +38 -0
- package/dist/components/XRCompositor/index.d.ts +5 -0
- package/dist/components/XRDebugView/index.d.ts +6 -0
- package/dist/components/XRLayer/index.d.ts +6 -0
- package/dist/components/XRMediaSurface/index.d.ts +27 -0
- package/dist/components/XRProvider/index.d.ts +13 -0
- package/dist/components/XRScene/defaults.d.ts +4 -0
- package/dist/components/XRScene/index.d.ts +15 -0
- package/dist/components/XRViewport/index.d.ts +101 -0
- package/dist/components/index.d.ts +26 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/constants/index.d.ts +25 -0
- package/dist/constants/renderLayers.d.ts +25 -0
- package/dist/entries/all.d.ts +15 -0
- package/dist/entries/core.d.ts +21 -0
- package/dist/entries/face-effects.d.ts +19 -0
- package/dist/entries/internal.d.ts +16 -0
- package/dist/entries/low-level.d.ts +30 -0
- package/dist/entries/tracking.d.ts +15 -0
- package/dist/face-effects.cjs +252 -0
- package/dist/face-effects.js +3542 -0
- package/dist/hasFullUnitUVBounds-BGvcFsQM.js +26 -0
- package/dist/hasFullUnitUVBounds-p1Dl0ejX.cjs +1 -0
- package/dist/hooks/faceResults.d.ts +39 -0
- package/dist/hooks/selectors.d.ts +33 -0
- package/dist/hooks/useCalibrationFaceTracker.d.ts +23 -0
- package/dist/hooks/useDebugger/index.d.ts +25 -0
- package/dist/hooks/useDebugger/types.d.ts +20 -0
- package/dist/hooks/useFaceBoundingBox.d.ts +11 -0
- package/dist/hooks/useFaceResults.d.ts +31 -0
- package/dist/hooks/useGestureEdge.d.ts +53 -0
- package/dist/hooks/useMediaRecorder.d.ts +40 -0
- package/dist/hooks/useTrackerContext.d.ts +35 -0
- package/dist/hooks/useXRModel.d.ts +15 -0
- package/dist/hooks/useXRSession/constants.d.ts +4 -0
- package/dist/hooks/useXRSession/facade.d.ts +39 -0
- package/dist/hooks/useXRSession/fns.d.ts +5 -0
- package/dist/hooks/useXRSession/index.d.ts +4 -0
- package/dist/hooks/useXRSession/registry.d.ts +52 -0
- package/dist/hooks/useXRSession/store.d.ts +40 -0
- package/dist/index-B5J4gzn2.cjs +1 -0
- package/dist/index-BD-JWMjE.js +1991 -0
- package/dist/index-BVBVg96G.cjs +1 -0
- package/dist/index-CYMj26Ej.js +1331 -0
- package/dist/index-DtwHElmG.js +157 -0
- package/dist/index-PtFmD6B8.cjs +1 -0
- package/dist/internal-DH5d1Xnq.cjs +1 -0
- package/dist/internal-KOKJDxCG.js +519 -0
- package/dist/internal.cjs +1 -0
- package/dist/internal.js +14 -0
- package/dist/low-level.cjs +1 -0
- package/dist/low-level.js +941 -0
- package/dist/main.cjs +1 -0
- package/dist/main.js +612 -0
- package/dist/models/Base/index.d.ts +25 -0
- package/dist/models/Body/index.d.ts +22 -0
- package/dist/models/Face/constants.d.ts +2 -0
- package/dist/models/Face/index.d.ts +45 -0
- package/dist/models/Face/landmarksData.d.ts +2 -0
- package/dist/models/Hand/GestureTracking.d.ts +21 -0
- package/dist/models/Hand/HandTracking.d.ts +21 -0
- package/dist/models/Segmentation/index.d.ts +75 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/modules/XRClient/index.d.ts +9 -0
- package/dist/modules/index.d.ts +1 -0
- package/dist/selectors-BHO6TnoC.js +59 -0
- package/dist/selectors-Bub0o3qy.cjs +1 -0
- package/dist/services/ApiService.d.ts +12 -0
- package/dist/services/MetricsCollector/CameraFPSTracker.d.ts +25 -0
- package/dist/services/MetricsCollector/MemoryTracker.d.ts +43 -0
- package/dist/services/MetricsCollector/RenderTimeTracker.d.ts +10 -0
- package/dist/services/MetricsCollector/ResolutionTracker.d.ts +26 -0
- package/dist/services/MetricsCollector/index.d.ts +59 -0
- package/dist/services/MetricsCollector/types.d.ts +47 -0
- package/dist/services/MetricsCollector/utils.d.ts +65 -0
- package/dist/texture/computeContainScale.d.ts +25 -0
- package/dist/texture/hasFullUnitUVBounds.d.ts +37 -0
- package/dist/texture/types.d.ts +20 -0
- package/dist/tracking.cjs +1 -0
- package/dist/tracking.js +344 -0
- package/dist/types/body.d.ts +3 -0
- package/dist/types/client.d.ts +3 -0
- package/dist/types/common.d.ts +9 -0
- package/dist/types/compositor.d.ts +23 -0
- package/dist/types/debugger.d.ts +83 -0
- package/dist/types/face.d.ts +67 -0
- package/dist/types/gesture-tracking.d.ts +15 -0
- package/dist/types/hand-tracking.d.ts +5 -0
- package/dist/types/headBinding.d.ts +67 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/logger.d.ts +4 -0
- package/dist/types/model.d.ts +46 -0
- package/dist/types/nodes.d.ts +35 -0
- package/dist/types/segmentation.d.ts +31 -0
- package/dist/types/session.d.ts +102 -0
- package/dist/types/tracking.d.ts +30 -0
- package/dist/types/util-types.d.ts +1 -0
- package/dist/useFaceResults-cpsSmKKc.js +81 -0
- package/dist/useFaceResults-hVUJYSEY.cjs +1 -0
- package/dist/utils/calibrationDebug.d.ts +9 -0
- package/dist/utils/dataURL.d.ts +6 -0
- package/dist/utils/drawMediaCover.d.ts +18 -0
- package/dist/utils/errors.d.ts +3 -0
- package/dist/utils/fns.d.ts +48 -0
- package/dist/utils/headBinding.d.ts +85 -0
- package/dist/utils/integrity.d.ts +43 -0
- package/dist/utils/landmarkBounds.d.ts +11 -0
- package/dist/utils/logger.d.ts +7 -0
- package/package.json +113 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FaceLandmarkerResult } from '@mediapipe/tasks-vision';
|
|
2
|
+
import { Texture } from 'three';
|
|
3
|
+
import { Coordinate } from './common';
|
|
4
|
+
import { Face, FaceRawTrackingData } from './face';
|
|
5
|
+
import { GestureData } from './gesture-tracking';
|
|
6
|
+
import { HandData } from './hand-tracking';
|
|
7
|
+
import { SegmentationData } from './segmentation';
|
|
8
|
+
export type XRModelNodeSegmentation = {
|
|
9
|
+
backgroundSegmentationMask?: Texture;
|
|
10
|
+
getMaskCanvas: () => HTMLCanvasElement | null;
|
|
11
|
+
getRawSegmentationData: () => SegmentationData | null;
|
|
12
|
+
/**
|
|
13
|
+
* Supply a high-resolution input source (raw <video>/<img>) so segmentation
|
|
14
|
+
* runs on native-resolution, uncropped frames instead of the small display-fit
|
|
15
|
+
* session feed. Pass `null`/`undefined` to revert to the shared feed canvas.
|
|
16
|
+
*/
|
|
17
|
+
setInputSource: (source: CanvasImageSource | null | undefined) => void;
|
|
18
|
+
};
|
|
19
|
+
export type XRModelNodeBodyTracking = {
|
|
20
|
+
getLandmarks2D: (bodyIndex?: number) => Coordinate[] | undefined;
|
|
21
|
+
};
|
|
22
|
+
export type XRModelNodeHandTracking = {
|
|
23
|
+
coordinates?: HandData["coordinates"];
|
|
24
|
+
gesture?: HandData["gesture"];
|
|
25
|
+
};
|
|
26
|
+
export type XRModelNodeGestureTracking = {
|
|
27
|
+
coordinates?: GestureData["coordinates"];
|
|
28
|
+
gesture?: GestureData["gesture"];
|
|
29
|
+
};
|
|
30
|
+
export type XRModelNodeFaceTracking = {
|
|
31
|
+
detectionResults?: FaceLandmarkerResult;
|
|
32
|
+
getDetectionResults: () => FaceLandmarkerResult | undefined;
|
|
33
|
+
getFace: (faceIndex?: number) => Face | undefined;
|
|
34
|
+
getRawFaceTrackingData: (faceIndex?: number) => FaceRawTrackingData | undefined;
|
|
35
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported segmentation pipeline types.
|
|
3
|
+
*
|
|
4
|
+
* Each value corresponds to a segmentation pipeline that can be enabled
|
|
5
|
+
* declaratively via the `<Segmentation type="…">` component. The runtime
|
|
6
|
+
* resolves the type to the corresponding xr-sdk segmentation pipeline.
|
|
7
|
+
*
|
|
8
|
+
* v1 supports `portrait` only. New types can be added without a breaking
|
|
9
|
+
* change as long as the existing values remain stable.
|
|
10
|
+
*/
|
|
11
|
+
export type SegmentationType = "portrait";
|
|
12
|
+
/**
|
|
13
|
+
* Runtime list of supported segmentation types. Used by the editor UI's
|
|
14
|
+
* type picker and by the design agent's approved-types validator.
|
|
15
|
+
*/
|
|
16
|
+
export declare const SEGMENTATION_TYPES: readonly ["portrait"];
|
|
17
|
+
export type SegmentationData = {
|
|
18
|
+
maskData: Uint8Array | null;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
export type SegmentationQuality = "low" | "medium" | "high";
|
|
23
|
+
export type SegmentationModelOptions = {
|
|
24
|
+
/**
|
|
25
|
+
* Quality vs performance tradeoff for segmentation processing
|
|
26
|
+
* - 'low': 0.5x resolution (fast, lower quality) - default
|
|
27
|
+
* - 'medium': 0.75x resolution (balanced)
|
|
28
|
+
* - 'high': 1.0x resolution (best quality, slower)
|
|
29
|
+
*/
|
|
30
|
+
quality?: SegmentationQuality;
|
|
31
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { XRMediaSource, XRModel } from '../constants';
|
|
2
|
+
import { XRMediaSourceKind } from './compositor';
|
|
3
|
+
import { BodyModelOptions } from './body';
|
|
4
|
+
import { FaceModelOptions } from './face';
|
|
5
|
+
import { SegmentationModelOptions } from './segmentation';
|
|
6
|
+
export declare enum SessionStatus {
|
|
7
|
+
ACTIVE = "ACTIVE",
|
|
8
|
+
DISABLED = "DISABLED"
|
|
9
|
+
}
|
|
10
|
+
export type Session = {
|
|
11
|
+
id: string;
|
|
12
|
+
status: SessionStatus;
|
|
13
|
+
networkId: string;
|
|
14
|
+
projectId: string;
|
|
15
|
+
createdAt: Date;
|
|
16
|
+
updatedAt: Date;
|
|
17
|
+
};
|
|
18
|
+
type SetMediaSourceWebcamParams = {
|
|
19
|
+
source: XRMediaSource.WEBCAM;
|
|
20
|
+
sourceKind?: "webcam";
|
|
21
|
+
};
|
|
22
|
+
type SetMediaSourceStreamParams = {
|
|
23
|
+
source: XRMediaSource.STREAM;
|
|
24
|
+
stream: MediaStream;
|
|
25
|
+
/**
|
|
26
|
+
* Mark the stream as static (e.g. a still image rendered to a captureStream).
|
|
27
|
+
* The session paints one frame, runs each model once to produce a detection
|
|
28
|
+
* snapshot, then pauses model RAF loops + the blit loop. Subsequent
|
|
29
|
+
* `setMediaSource` calls with a new static stream repeat the pulse.
|
|
30
|
+
*/
|
|
31
|
+
static?: boolean;
|
|
32
|
+
sourceKind?: "webcam" | XRMediaSourceKind;
|
|
33
|
+
};
|
|
34
|
+
export type SetMediaSourceParams = SetMediaSourceWebcamParams | SetMediaSourceStreamParams;
|
|
35
|
+
export type SetFrameSourceParams = {
|
|
36
|
+
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
37
|
+
sourceKind?: "webcam" | XRMediaSourceKind;
|
|
38
|
+
};
|
|
39
|
+
export type RegisterXRPipelineParams = Array<{
|
|
40
|
+
model: XRModel;
|
|
41
|
+
} | {
|
|
42
|
+
model: XRModel.BODY_TRACKER;
|
|
43
|
+
options?: BodyModelOptions;
|
|
44
|
+
} | {
|
|
45
|
+
model: XRModel.FACE_TRACKER;
|
|
46
|
+
options?: FaceModelOptions;
|
|
47
|
+
} | {
|
|
48
|
+
model: XRModel.BACKGROUND_SEGMENTATION;
|
|
49
|
+
options?: SegmentationModelOptions;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Public XR session surface. Returned by `useXRContext().session` and used
|
|
53
|
+
* by tracker components, the editor preview, and example apps.
|
|
54
|
+
*
|
|
55
|
+
* The reference is STABLE for the entire lifetime of the enclosing
|
|
56
|
+
* `XRProvider` — internal state changes do NOT cause `session` to take a
|
|
57
|
+
* new identity. Mutable state is exposed via selector hooks
|
|
58
|
+
* (`useXRReady`, `useXRError`, `useXRCameraTexture`, `useXRModelNode`,
|
|
59
|
+
* `useXRPipelineSnapshot`) so consumers only re-render on the slice they
|
|
60
|
+
* care about.
|
|
61
|
+
*
|
|
62
|
+
* Lifecycle:
|
|
63
|
+
*
|
|
64
|
+
* idle ──setMediaSource──> media-bound ──start──> running ──stop──> stopped
|
|
65
|
+
* ↑ │
|
|
66
|
+
* └────────────reset──────────────────────┘
|
|
67
|
+
*/
|
|
68
|
+
export type XRSession = {
|
|
69
|
+
setMediaSource: (params: SetMediaSourceParams) => Promise<void>;
|
|
70
|
+
setFrameSource: (params: SetFrameSourceParams) => Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Idempotent. Calling `start()` from the `running` state is a no-op.
|
|
73
|
+
* Throws if called from `idle` (no media source bound).
|
|
74
|
+
*/
|
|
75
|
+
start: () => Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Halts the per-frame run loop and releases the input video / tracks.
|
|
78
|
+
* Preserves the model registry — a subsequent `setMediaSource` + `start`
|
|
79
|
+
* resumes with the same models.
|
|
80
|
+
*/
|
|
81
|
+
stop: () => Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* `stop()` + clear the model registry. Returns the session to `idle`.
|
|
84
|
+
*/
|
|
85
|
+
reset: () => Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated Prefer `useXRModel(modelName, options)`. Kept as an
|
|
88
|
+
* additive shim for backwards compatibility — multiple calls are
|
|
89
|
+
* idempotent and never evict previously registered models.
|
|
90
|
+
*/
|
|
91
|
+
registerXRPipeline: (params: RegisterXRPipelineParams) => Promise<void>;
|
|
92
|
+
getModelNode: <T extends Record<string, unknown>>(modelCode: XRModel) => T;
|
|
93
|
+
/**
|
|
94
|
+
* Synchronously render the current scene and read the canvas back as a PNG
|
|
95
|
+
* dataURL. Use from a gesture or state callback (e.g. on capture trigger)
|
|
96
|
+
* to grab the visible frame as a photo. Returns the dataURL string.
|
|
97
|
+
*
|
|
98
|
+
* Throws `XRError` if called before `<XRScene>` has mounted.
|
|
99
|
+
*/
|
|
100
|
+
captureFrame: () => string;
|
|
101
|
+
};
|
|
102
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracking target enumerations.
|
|
3
|
+
*
|
|
4
|
+
* A `TrackingAnchor`'s `target` prop is a typed string in the form
|
|
5
|
+
* `<tracker>.<landmark>`. The namespace prefix (`face.`, `hand.`, `body.`)
|
|
6
|
+
* is used by the renderer and the editor to validate that an anchor's
|
|
7
|
+
* target matches the type of its nearest ancestor tracker.
|
|
8
|
+
*
|
|
9
|
+
* The lists below are the v1 enumeration. New entries can be added
|
|
10
|
+
* non-breakingly as additional landmarks/joints become supported.
|
|
11
|
+
*/
|
|
12
|
+
export type FaceTarget = "face.forehead" | "face.noseTip" | "face.chin" | "face.leftEyeOuter" | "face.rightEyeOuter" | "face.leftCheek" | "face.rightCheek" | "face.mouthCenter";
|
|
13
|
+
export type HandTarget = "hand.wrist" | "hand.thumbTip" | "hand.indexTip" | "hand.middleTip" | "hand.ringTip" | "hand.pinkyTip" | "hand.indexBase" | "hand.middleBase" | "hand.ringBase" | "hand.pinkyBase";
|
|
14
|
+
export type BodyTarget = "body.head" | "body.neck" | "body.leftShoulder" | "body.rightShoulder" | "body.leftElbow" | "body.rightElbow" | "body.leftWrist" | "body.rightWrist" | "body.leftHip" | "body.rightHip" | "body.leftKnee" | "body.rightKnee" | "body.leftAnkle" | "body.rightAnkle";
|
|
15
|
+
/**
|
|
16
|
+
* Combined tracking target type used by the `TrackingAnchor` component.
|
|
17
|
+
*/
|
|
18
|
+
export type TrackingTarget = FaceTarget | HandTarget | BodyTarget;
|
|
19
|
+
/**
|
|
20
|
+
* Tracker type identifier exposed via tracker context.
|
|
21
|
+
*/
|
|
22
|
+
export type TrackerType = "face" | "hand" | "body";
|
|
23
|
+
export declare const FACE_TARGETS: readonly ["face.forehead", "face.noseTip", "face.chin", "face.leftEyeOuter", "face.rightEyeOuter", "face.leftCheek", "face.rightCheek", "face.mouthCenter"];
|
|
24
|
+
export declare const HAND_TARGETS: readonly ["hand.wrist", "hand.thumbTip", "hand.indexTip", "hand.middleTip", "hand.ringTip", "hand.pinkyTip", "hand.indexBase", "hand.middleBase", "hand.ringBase", "hand.pinkyBase"];
|
|
25
|
+
export declare const BODY_TARGETS: readonly ["body.head", "body.neck", "body.leftShoulder", "body.rightShoulder", "body.leftElbow", "body.rightElbow", "body.leftWrist", "body.rightWrist", "body.leftHip", "body.rightHip", "body.leftKnee", "body.rightKnee", "body.leftAnkle", "body.rightAnkle"];
|
|
26
|
+
/**
|
|
27
|
+
* Returns the tracker namespace for a given target string, or `null` if
|
|
28
|
+
* the target does not match any known prefix.
|
|
29
|
+
*/
|
|
30
|
+
export declare const getTargetNamespace: (target: string) => TrackerType | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Class<T> = new (...args: any[]) => T;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useState as x, useEffect as g } from "react";
|
|
2
|
+
const E = (t) => {
|
|
3
|
+
if (t.length === 0)
|
|
4
|
+
return { minX: 0, minY: 0, maxX: 0, maxY: 0, width: 0, height: 0, centerX: 0, centerY: 0 };
|
|
5
|
+
let n = 1, e = 1, s = 0, a = 0;
|
|
6
|
+
for (let o = 0; o < t.length; o++) {
|
|
7
|
+
const { x: l, y: c } = t[o];
|
|
8
|
+
l < n && (n = l), c < e && (e = c), l > s && (s = l), c > a && (a = c);
|
|
9
|
+
}
|
|
10
|
+
const i = Math.max(0, s - n), h = Math.max(0, a - e);
|
|
11
|
+
return {
|
|
12
|
+
minX: n,
|
|
13
|
+
minY: e,
|
|
14
|
+
maxX: s,
|
|
15
|
+
maxY: a,
|
|
16
|
+
width: i,
|
|
17
|
+
height: h,
|
|
18
|
+
centerX: n + i * 0.5,
|
|
19
|
+
centerY: e + h * 0.5
|
|
20
|
+
};
|
|
21
|
+
}, N = (t, n) => t.map((e) => ({
|
|
22
|
+
landmarks: e,
|
|
23
|
+
bounds: E(e)
|
|
24
|
+
})), P = {
|
|
25
|
+
publishedCount: 0,
|
|
26
|
+
// -Infinity, not 0: the first non-empty frame must publish regardless of its
|
|
27
|
+
// timestamp (a frame at now=0 would otherwise be throttled against 0).
|
|
28
|
+
lastPublishAt: -1 / 0,
|
|
29
|
+
lastNonEmptyAt: -1
|
|
30
|
+
}, X = (t, n, e, s, a) => {
|
|
31
|
+
if (n > 0) {
|
|
32
|
+
const i = { ...t, lastNonEmptyAt: e };
|
|
33
|
+
return e - t.lastPublishAt >= s ? {
|
|
34
|
+
action: "publish",
|
|
35
|
+
state: { publishedCount: n, lastPublishAt: e, lastNonEmptyAt: e }
|
|
36
|
+
} : { action: "none", state: i };
|
|
37
|
+
}
|
|
38
|
+
return t.publishedCount === 0 ? { action: "none", state: t } : t.lastNonEmptyAt >= 0 && e - t.lastNonEmptyAt < a ? { action: "none", state: t } : {
|
|
39
|
+
action: "clear",
|
|
40
|
+
// Reset the throttle anchor so the next reacquired face publishes at once.
|
|
41
|
+
state: { publishedCount: 0, lastPublishAt: -1 / 0, lastNonEmptyAt: t.lastNonEmptyAt }
|
|
42
|
+
};
|
|
43
|
+
}, R = ({
|
|
44
|
+
node: t,
|
|
45
|
+
enabled: n,
|
|
46
|
+
selectedSource: e,
|
|
47
|
+
throttleMs: s = 16,
|
|
48
|
+
holdMs: a = 250,
|
|
49
|
+
onFrame: i
|
|
50
|
+
}) => {
|
|
51
|
+
const [h, o] = x([]);
|
|
52
|
+
return g(() => {
|
|
53
|
+
if (!n || !t) {
|
|
54
|
+
o((u) => u.length === 0 ? u : []);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let l = 0, c, m = P;
|
|
58
|
+
const p = (u, r, f) => {
|
|
59
|
+
const { action: A, state: y } = X(m, u, r, s, a);
|
|
60
|
+
m = y, A === "publish" ? o(f) : A === "clear" && o((d) => d.length === 0 ? d : []);
|
|
61
|
+
}, b = () => {
|
|
62
|
+
l = requestAnimationFrame(b), i == null || i();
|
|
63
|
+
const u = performance.now(), r = t.getDetectionResults();
|
|
64
|
+
if (!r) {
|
|
65
|
+
p(0, u, []), c = r;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (r === c) return;
|
|
69
|
+
c = r;
|
|
70
|
+
const f = N(
|
|
71
|
+
r.faceLandmarks ?? []
|
|
72
|
+
);
|
|
73
|
+
p(f.length, u, f);
|
|
74
|
+
};
|
|
75
|
+
return l = requestAnimationFrame(b), () => cancelAnimationFrame(l);
|
|
76
|
+
}, [n, t, e, s, i]), h;
|
|
77
|
+
};
|
|
78
|
+
export {
|
|
79
|
+
E as c,
|
|
80
|
+
R as u
|
|
81
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const y=require("react"),x=t=>{if(t.length===0)return{minX:0,minY:0,maxX:0,maxY:0,width:0,height:0,centerX:0,centerY:0};let n=1,e=1,s=0,a=0;for(let u=0;u<t.length;u++){const{x:c,y:l}=t[u];c<n&&(n=c),l<e&&(e=l),c>s&&(s=c),l>a&&(a=l)}const i=Math.max(0,s-n),h=Math.max(0,a-e);return{minX:n,minY:e,maxX:s,maxY:a,width:i,height:h,centerX:n+i*.5,centerY:e+h*.5}},E=(t,n)=>t.map(e=>({landmarks:e,bounds:x(e)})),N={publishedCount:0,lastPublishAt:-1/0,lastNonEmptyAt:-1},R=(t,n,e,s,a)=>{if(n>0){const i={...t,lastNonEmptyAt:e};return e-t.lastPublishAt>=s?{action:"publish",state:{publishedCount:n,lastPublishAt:e,lastNonEmptyAt:e}}:{action:"none",state:i}}return t.publishedCount===0?{action:"none",state:t}:t.lastNonEmptyAt>=0&&e-t.lastNonEmptyAt<a?{action:"none",state:t}:{action:"clear",state:{publishedCount:0,lastPublishAt:-1/0,lastNonEmptyAt:t.lastNonEmptyAt}}},P=({node:t,enabled:n,selectedSource:e,throttleMs:s=16,holdMs:a=250,onFrame:i})=>{const[h,u]=y.useState([]);return y.useEffect(()=>{if(!n||!t){u(o=>o.length===0?o:[]);return}let c=0,l,f=N;const p=(o,r,m)=>{const{action:A,state:g}=R(f,o,r,s,a);f=g,A==="publish"?u(m):A==="clear"&&u(d=>d.length===0?d:[])},b=()=>{c=requestAnimationFrame(b),i==null||i();const o=performance.now(),r=t.getDetectionResults();if(!r){p(0,o,[]),l=r;return}if(r===l)return;l=r;const m=E(r.faceLandmarks??[]);p(m.length,o,m)};return c=requestAnimationFrame(b),()=>cancelAnimationFrame(c)},[n,t,e,s,i]),h};exports.computeLandmarkBounds=x;exports.useFaceResults=P;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type CalibrationDebugEvent = {
|
|
2
|
+
label: string;
|
|
3
|
+
payload: Record<string, unknown>;
|
|
4
|
+
source: string;
|
|
5
|
+
ts: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function recordCalibrationDebugEvent(source: string, label: string, payload: Record<string, unknown>): void;
|
|
8
|
+
export declare function readCalibrationDebugEvents(limit?: number): CalibrationDebugEvent[];
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a `data:` URL (e.g. a captured canvas frame) into a `File`, ready to
|
|
3
|
+
* upload or hand to the Web Share API. Parses the MIME from the URL header and
|
|
4
|
+
* base64-decodes the payload.
|
|
5
|
+
*/
|
|
6
|
+
export declare const dataURLtoFile: (dataUrl: string, filename: string) => File;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { XRMediaLayout } from '../types/compositor';
|
|
2
|
+
/**
|
|
3
|
+
* Blit an image/video onto a 2D canvas using a precomputed cover/contain
|
|
4
|
+
* layout, optionally mirrored. Resizes the canvas to the layout's
|
|
5
|
+
* destination size in place. The canvas is write-only (no getImageData),
|
|
6
|
+
* so it stays GPU-uploadable.
|
|
7
|
+
*
|
|
8
|
+
* `filter` applies a CSS canvas filter (e.g. brightness boost for a dark
|
|
9
|
+
* webcam feed) only for the duration of the draw, then resets. Off by
|
|
10
|
+
* default — opt in per call site.
|
|
11
|
+
*/
|
|
12
|
+
export declare const drawMediaCover: ({ canvas, layout, media, mirrored, filter, }: {
|
|
13
|
+
canvas: HTMLCanvasElement;
|
|
14
|
+
layout: XRMediaLayout;
|
|
15
|
+
media: HTMLImageElement | HTMLVideoElement;
|
|
16
|
+
mirrored: boolean;
|
|
17
|
+
filter?: string;
|
|
18
|
+
}) => void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { NormalizedLandmark } from '@mediapipe/tasks-vision';
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
export declare const rangeMinusOneToPositiveOne: (number: number) => number;
|
|
4
|
+
export declare const rangeZeroToOne: (number: number) => number;
|
|
5
|
+
export declare const rotationMatrixToEulerAngles: (facialTransformationMatrix: Array<number>) => {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
z: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const drawImageDataCover: (canvas: HTMLCanvasElement, input: ImageData | HTMLVideoElement) => void;
|
|
11
|
+
/**
|
|
12
|
+
* In-place variant of unprojectToZZero. Writes the world-space point into
|
|
13
|
+
* `out` and returns it; allocates nothing on the hot path. The
|
|
14
|
+
* trackers call this once per landmark per frame (~468× for face), so the
|
|
15
|
+
* allocating variant below shows up as a real GC pressure source.
|
|
16
|
+
*/
|
|
17
|
+
export declare const unprojectToZZeroInto: ({ x, y, z }: NormalizedLandmark, camera: THREE.Camera, out: THREE.Vector3) => THREE.Vector3;
|
|
18
|
+
/**
|
|
19
|
+
* Allocating wrapper kept for backwards compatibility with any
|
|
20
|
+
* consumer-facing API surface. New call sites in the trackers use
|
|
21
|
+
* unprojectToZZeroInto with a pre-allocated out vector.
|
|
22
|
+
*/
|
|
23
|
+
export declare const unprojectToZZero: (landmark: NormalizedLandmark, camera: THREE.Camera) => THREE.Vector3;
|
|
24
|
+
export declare const smoothScale: (originalScale: number, newScale: number, alpha?: number) => number;
|
|
25
|
+
/**
|
|
26
|
+
* Downscales the shared source canvas into a smaller reusable analysis canvas
|
|
27
|
+
* before handing it to MediaPipe. Landmark models don't need the full
|
|
28
|
+
* render-resolution frame — analyzing fewer pixels cuts per-inference cost (and
|
|
29
|
+
* the GPU→CPU readback scales with it) with no accuracy loss for
|
|
30
|
+
* hand/gesture/pose at these sizes. The analysis canvas is allocated once and
|
|
31
|
+
* only reallocated when the target dimensions change, so there's no per-frame
|
|
32
|
+
* churn.
|
|
33
|
+
*
|
|
34
|
+
* FaceTracker inlines its own equivalent (its analysis dimension depends on
|
|
35
|
+
* `numFaces`); Hand/Gesture/Body share this one.
|
|
36
|
+
*/
|
|
37
|
+
export declare class AnalysisDownscaler {
|
|
38
|
+
private readonly maxDimension;
|
|
39
|
+
private canvas;
|
|
40
|
+
constructor(maxDimension: number);
|
|
41
|
+
/**
|
|
42
|
+
* Returns a downscaled copy of `source` capped to `maxDimension` on its
|
|
43
|
+
* longest side (or `source` unchanged if it's already within the cap, or if a
|
|
44
|
+
* 2D context can't be obtained). The returned canvas is reused across calls.
|
|
45
|
+
*/
|
|
46
|
+
draw(source: OffscreenCanvas | HTMLCanvasElement): OffscreenCanvas | HTMLCanvasElement;
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { FaceTrackingResult } from '../types/face';
|
|
2
|
+
import { HeadBindingAdaptiveValues, HeadBindingCubeModel, HeadBindingCubePose, HeadBindingDebugMetrics, HeadBindingSolveResult, HeadBindingShape } from '../types/headBinding';
|
|
3
|
+
export declare const computeUniversalOccluderScale: ({ baseScale, pitchRadians, }: {
|
|
4
|
+
baseScale: [number, number, number];
|
|
5
|
+
pitchRadians: number;
|
|
6
|
+
}) => [number, number, number];
|
|
7
|
+
export declare const computeUniversalYawOffsetX: ({ yawRadians }: {
|
|
8
|
+
yawRadians: number;
|
|
9
|
+
}) => number;
|
|
10
|
+
export declare const computeUniversalPitchOffsetY: ({ pitchRadians, }: {
|
|
11
|
+
pitchRadians: number;
|
|
12
|
+
}) => number;
|
|
13
|
+
export declare const computeUniversalHeadDepthOffset: ({ baseOffset, yawRadians, }: {
|
|
14
|
+
baseOffset: number;
|
|
15
|
+
yawRadians: number;
|
|
16
|
+
}) => number;
|
|
17
|
+
export declare const computeAdaptiveVerticalFit: ({ baseOffsetY, baseScaleY, face, }: {
|
|
18
|
+
baseOffsetY: number;
|
|
19
|
+
baseScaleY: number;
|
|
20
|
+
face: FaceTrackingResult | null;
|
|
21
|
+
}) => HeadBindingAdaptiveValues;
|
|
22
|
+
export declare const computeHeadBindingShape: ({ face, offsetX, offsetY, scaleX, scaleY, }: {
|
|
23
|
+
face: FaceTrackingResult;
|
|
24
|
+
offsetX: number;
|
|
25
|
+
offsetY: number;
|
|
26
|
+
scaleX: number;
|
|
27
|
+
scaleY: number;
|
|
28
|
+
}) => HeadBindingShape;
|
|
29
|
+
export declare const computeHeadBindingCubePose: ({ lockDepth, lockRotation, model, face, height, headDepthOffset, offsetX, offsetY, rotationOffsetX, rotationOffsetY, rotationOffsetZ, scaleX, scaleY, scaleZ, skullCenterMode, skullVerticalBias, width, }: {
|
|
30
|
+
lockDepth: boolean;
|
|
31
|
+
lockRotation: boolean;
|
|
32
|
+
model: HeadBindingCubeModel;
|
|
33
|
+
face: FaceTrackingResult;
|
|
34
|
+
height: number;
|
|
35
|
+
headDepthOffset: number;
|
|
36
|
+
offsetX: number;
|
|
37
|
+
offsetY: number;
|
|
38
|
+
rotationOffsetX: number;
|
|
39
|
+
rotationOffsetY: number;
|
|
40
|
+
rotationOffsetZ: number;
|
|
41
|
+
scaleX: number;
|
|
42
|
+
scaleY: number;
|
|
43
|
+
scaleZ: number;
|
|
44
|
+
skullCenterMode: boolean;
|
|
45
|
+
skullVerticalBias: number;
|
|
46
|
+
width: number;
|
|
47
|
+
}) => HeadBindingCubePose;
|
|
48
|
+
export declare const solveHeadBinding: ({ lockDepth, lockRotation, model, face, height, headDepthOffset, offsetX, offsetY, rotationOffsetX, rotationOffsetY, rotationOffsetZ, scaleX, scaleY, scaleZ, skullCenterMode, skullVerticalBias, width, }: {
|
|
49
|
+
lockDepth: boolean;
|
|
50
|
+
lockRotation: boolean;
|
|
51
|
+
model: HeadBindingCubeModel;
|
|
52
|
+
face: FaceTrackingResult;
|
|
53
|
+
height: number;
|
|
54
|
+
headDepthOffset: number;
|
|
55
|
+
offsetX: number;
|
|
56
|
+
offsetY: number;
|
|
57
|
+
rotationOffsetX: number;
|
|
58
|
+
rotationOffsetY: number;
|
|
59
|
+
rotationOffsetZ: number;
|
|
60
|
+
scaleX: number;
|
|
61
|
+
scaleY: number;
|
|
62
|
+
scaleZ: number;
|
|
63
|
+
skullCenterMode: boolean;
|
|
64
|
+
skullVerticalBias: number;
|
|
65
|
+
width: number;
|
|
66
|
+
}) => HeadBindingSolveResult;
|
|
67
|
+
export declare const computeHeadBindingDebugMetrics: ({ lockDepth, lockRotation, model, face, height, headDepthOffset, offsetX, offsetY, rotationOffsetX, rotationOffsetY, rotationOffsetZ, scaleX, scaleY, scaleZ, skullCenterMode, skullVerticalBias, width, }: {
|
|
68
|
+
lockDepth: boolean;
|
|
69
|
+
lockRotation: boolean;
|
|
70
|
+
model: HeadBindingCubeModel;
|
|
71
|
+
face: FaceTrackingResult;
|
|
72
|
+
height: number;
|
|
73
|
+
headDepthOffset: number;
|
|
74
|
+
offsetX: number;
|
|
75
|
+
offsetY: number;
|
|
76
|
+
rotationOffsetX: number;
|
|
77
|
+
rotationOffsetY: number;
|
|
78
|
+
rotationOffsetZ: number;
|
|
79
|
+
scaleX: number;
|
|
80
|
+
scaleY: number;
|
|
81
|
+
scaleZ: number;
|
|
82
|
+
skullCenterMode: boolean;
|
|
83
|
+
skullVerticalBias: number;
|
|
84
|
+
width: number;
|
|
85
|
+
}) => HeadBindingDebugMetrics;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subresource-integrity verification for CDN-hosted model + mesh assets.
|
|
3
|
+
*
|
|
4
|
+
* The SDK fetches MediaPipe tracker weights, the canonical face mesh, and
|
|
5
|
+
* the head-occluder GLB from `cdn.vincentt.studio`. None of those are
|
|
6
|
+
* served with SRI script-attributes (they aren't <script> tags) and the
|
|
7
|
+
* S3+CloudFront setup does not sign content, so a CDN/origin compromise
|
|
8
|
+
* would silently backdoor ML inference and 3D models for every viewer.
|
|
9
|
+
*
|
|
10
|
+
* This module verifies fetched bytes against a hash baked into the SDK
|
|
11
|
+
* at publish time. The manifest is generated by
|
|
12
|
+
* `scripts/generate-integrity-manifest.ts` — when an asset is republished
|
|
13
|
+
* on the CDN, re-run the script and commit the new hash with the new SDK
|
|
14
|
+
* version. The hash travels with the SDK code, not with the asset.
|
|
15
|
+
*
|
|
16
|
+
* Consumers: the canonical face OBJ, MediaPipe tracker .task files
|
|
17
|
+
* (fed via `modelAssetBuffer`), and the head-occluder GLB (parsed with
|
|
18
|
+
* GLTFLoader.parse from the verified buffer). drei's `useGLTF` is
|
|
19
|
+
* bypassed because it owns the fetch — we need to own it to verify.
|
|
20
|
+
*/
|
|
21
|
+
export type IntegrityHash = `sha384-${string}`;
|
|
22
|
+
/**
|
|
23
|
+
* Keys are URL pathnames relative to the asset origin
|
|
24
|
+
* (e.g. "/assets/xr/canonical_face_model.obj"). Populated by
|
|
25
|
+
* `scripts/generate-integrity-manifest.ts`. An empty manifest puts
|
|
26
|
+
* fetchAndVerify in best-effort mode — once entries are added, missing
|
|
27
|
+
* entries can be promoted to hard errors.
|
|
28
|
+
*/
|
|
29
|
+
export declare const INTEGRITY_MANIFEST: Record<string, IntegrityHash>;
|
|
30
|
+
/**
|
|
31
|
+
* Fetches a URL and verifies its body against the manifest. Returns the
|
|
32
|
+
* ArrayBuffer on success. Throws if the fetch fails or the manifest hash
|
|
33
|
+
* doesn't match the computed SHA-384.
|
|
34
|
+
*
|
|
35
|
+
* URLs with no manifest entry pass through with a warning so the SDK
|
|
36
|
+
* stays usable while the manifest is being populated.
|
|
37
|
+
*/
|
|
38
|
+
export declare function fetchAndVerify(url: string): Promise<ArrayBuffer>;
|
|
39
|
+
/**
|
|
40
|
+
* fetchAndVerify variant that returns a Uint8Array view — what MediaPipe's
|
|
41
|
+
* `modelAssetBuffer` expects.
|
|
42
|
+
*/
|
|
43
|
+
export declare function fetchAndVerifyBytes(url: string): Promise<Uint8Array>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NormalizedLandmark } from '@mediapipe/tasks-vision';
|
|
2
|
+
import { FaceBounds } from '../types/face';
|
|
3
|
+
/**
|
|
4
|
+
* Axis-aligned bounds of a set of normalized face landmarks (0..1 across
|
|
5
|
+
* both axes). Returns min/max plus derived width/height/center so callers
|
|
6
|
+
* can adapt to whichever shape they need (`FaceBounds`, `BoundingBox`, ...).
|
|
7
|
+
*
|
|
8
|
+
* Empty input yields a zero-size box at the origin rather than the inverted
|
|
9
|
+
* `min=1,max=0` seed, so downstream width/height stay non-negative.
|
|
10
|
+
*/
|
|
11
|
+
export declare const computeLandmarkBounds: (landmarks: NormalizedLandmark[]) => FaceBounds;
|
package/package.json
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vincentt-xr/sdk",
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
|
+
"description": "XR SDK by Vincentt",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"Vincentt",
|
|
8
|
+
"Web AR",
|
|
9
|
+
"XR",
|
|
10
|
+
"SDK"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"files": [
|
|
15
|
+
"/dist",
|
|
16
|
+
"GROUNDING.md"
|
|
17
|
+
],
|
|
18
|
+
"main": "./dist/main.cjs",
|
|
19
|
+
"module": "./dist/main.js",
|
|
20
|
+
"types": "./dist/entries/core.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/entries/core.d.ts",
|
|
24
|
+
"import": "./dist/main.js",
|
|
25
|
+
"require": "./dist/main.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./tracking": {
|
|
28
|
+
"types": "./dist/entries/tracking.d.ts",
|
|
29
|
+
"import": "./dist/tracking.js",
|
|
30
|
+
"require": "./dist/tracking.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./face-effects": {
|
|
33
|
+
"types": "./dist/entries/face-effects.d.ts",
|
|
34
|
+
"import": "./dist/face-effects.js",
|
|
35
|
+
"require": "./dist/face-effects.cjs"
|
|
36
|
+
},
|
|
37
|
+
"./low-level": {
|
|
38
|
+
"types": "./dist/entries/low-level.d.ts",
|
|
39
|
+
"import": "./dist/low-level.js",
|
|
40
|
+
"require": "./dist/low-level.cjs"
|
|
41
|
+
},
|
|
42
|
+
"./internal": {
|
|
43
|
+
"types": "./dist/entries/internal.d.ts",
|
|
44
|
+
"import": "./dist/internal.js",
|
|
45
|
+
"require": "./dist/internal.cjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@mediapipe/tasks-vision": "^0.10.18",
|
|
50
|
+
"@react-three/drei": "9.105.6",
|
|
51
|
+
"@react-three/fiber": "8.18.0",
|
|
52
|
+
"react": "^18.2.0",
|
|
53
|
+
"react-dom": "^18.2.0",
|
|
54
|
+
"three": "0.173.0"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@mediapipe/tasks-vision": {
|
|
58
|
+
"optional": false
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^22.9.0",
|
|
63
|
+
"@types/react": "^18.3.12",
|
|
64
|
+
"@types/react-dom": "^18.3.1",
|
|
65
|
+
"@types/three": "0.173.0",
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^8.14.0",
|
|
67
|
+
"@typescript-eslint/parser": "^8.14.0",
|
|
68
|
+
"@vitejs/plugin-react": "^4.3.3",
|
|
69
|
+
"commit-and-tag-version": "^12.5.0",
|
|
70
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
71
|
+
"eslint": "^9.14.0",
|
|
72
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
73
|
+
"eslint-config-airbnb-typescript": "^18.0.0",
|
|
74
|
+
"eslint-config-prettier": "^9.1.0",
|
|
75
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
76
|
+
"eslint-plugin-react": "^7.37.2",
|
|
77
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
78
|
+
"eslint-plugin-react-refresh": "^0.4.14",
|
|
79
|
+
"husky": "^9.1.6",
|
|
80
|
+
"lint-staged": "^15.2.10",
|
|
81
|
+
"prettier": "3.3.3",
|
|
82
|
+
"qrcode-terminal": "^0.12.0",
|
|
83
|
+
"react-router-dom": "^6.28.0",
|
|
84
|
+
"typescript": "^5.6.3",
|
|
85
|
+
"vite": "^5.4.11",
|
|
86
|
+
"vite-plugin-dts": "^4.3.0",
|
|
87
|
+
"vite-tsconfig-paths": "^5.1.2",
|
|
88
|
+
"vitest": "^2.1.5"
|
|
89
|
+
},
|
|
90
|
+
"lint-staged": {
|
|
91
|
+
"**/*": "prettier --write --ignore-unknown"
|
|
92
|
+
},
|
|
93
|
+
"config": {
|
|
94
|
+
"commitizen": {
|
|
95
|
+
"path": "./node_modules/cz-conventional-changelog"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"dev": "vite --config example/vite.config.ts --mode dev",
|
|
100
|
+
"preview:tunnel": "node example/scripts/preview.mjs",
|
|
101
|
+
"preview:local": "node example/scripts/preview.mjs --local",
|
|
102
|
+
"build": "vite build",
|
|
103
|
+
"build:watch": "vite build --watch",
|
|
104
|
+
"format:check": "prettier --check .",
|
|
105
|
+
"format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}' 'lib/**/*.{js,jsx,ts,tsx}'",
|
|
106
|
+
"test": "vitest run",
|
|
107
|
+
"test:watch": "vitest",
|
|
108
|
+
"lint:check": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
109
|
+
"example:build": "vite build --config example/vite.config.ts",
|
|
110
|
+
"example:preview": "vite preview --config example/vite.config.ts --outDir dist-example",
|
|
111
|
+
"release": "commit-and-tag-version"
|
|
112
|
+
}
|
|
113
|
+
}
|