talking-head-studio 0.4.10 → 0.4.12

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.
Files changed (178) hide show
  1. package/README.md +299 -337
  2. package/dist/TalkingHead.d.ts +44 -28
  3. package/dist/TalkingHead.js +21 -2
  4. package/dist/TalkingHead.web.d.ts +37 -4
  5. package/dist/TalkingHead.web.js +28 -8
  6. package/dist/TalkingHeadVisualization.d.ts +22 -0
  7. package/dist/TalkingHeadVisualization.js +30 -10
  8. package/dist/api/studioApi.d.ts +12 -1
  9. package/dist/api/studioApi.js +41 -28
  10. package/dist/appearance/apply.js +2 -3
  11. package/dist/appearance/matchers.js +1 -2
  12. package/dist/appearance/schema.js +1 -2
  13. package/dist/contract.d.ts +14 -0
  14. package/dist/contract.js +30 -0
  15. package/dist/core/avatar/avatarCapabilities.d.ts +60 -0
  16. package/dist/core/avatar/avatarCapabilities.js +100 -0
  17. package/dist/core/avatar/backend.d.ts +130 -0
  18. package/dist/core/avatar/backend.js +4 -0
  19. package/dist/core/avatar/backends/gaussian.d.ts +49 -0
  20. package/dist/core/avatar/backends/gaussian.js +293 -0
  21. package/dist/core/avatar/backends/index.d.ts +3 -0
  22. package/dist/core/avatar/backends/index.js +7 -0
  23. package/dist/core/avatar/backends/morphTarget.d.ts +39 -0
  24. package/dist/core/avatar/backends/morphTarget.js +179 -0
  25. package/dist/core/avatar/faceControls.d.ts +40 -0
  26. package/dist/core/avatar/faceControls.js +138 -0
  27. package/dist/core/avatar/motion.d.ts +1713 -0
  28. package/dist/core/avatar/motion.js +550 -0
  29. package/dist/core/avatar/motionRuntime.d.ts +46 -0
  30. package/dist/core/avatar/motionRuntime.js +84 -0
  31. package/dist/core/avatar/schema.d.ts +78 -0
  32. package/dist/core/avatar/schema.js +134 -0
  33. package/dist/core/avatar/visemes.d.ts +47 -1
  34. package/dist/core/avatar/visemes.js +114 -1
  35. package/dist/editor/AvatarCanvas.js +93 -3
  36. package/dist/editor/AvatarEditor.native.js +19 -9
  37. package/dist/editor/AvatarModel.js +2 -2
  38. package/dist/editor/FaceSqueezeEditor.d.ts +3 -1
  39. package/dist/editor/FaceSqueezeEditor.js +195 -121
  40. package/dist/editor/FaceSqueezeEditor.web.d.ts +3 -1
  41. package/dist/editor/FaceSqueezeEditor.web.js +32 -30
  42. package/dist/editor/RigidAccessory.js +18 -4
  43. package/dist/editor/SkinnedClothing.js +19 -9
  44. package/dist/editor/boneLockedDrag.d.ts +11 -0
  45. package/dist/editor/boneLockedDrag.js +68 -0
  46. package/dist/editor/boneSnap.js +22 -12
  47. package/dist/editor/boneSnap.web.d.ts +27 -0
  48. package/dist/editor/boneSnap.web.js +99 -0
  49. package/dist/editor/index.web.d.ts +10 -0
  50. package/dist/editor/index.web.js +26 -0
  51. package/dist/editor/sounds/haha.wav +0 -0
  52. package/dist/editor/sounds/owie.wav +0 -0
  53. package/dist/editor/sounds/stop.wav +0 -0
  54. package/dist/editor/studioTheme.d.ts +14 -14
  55. package/dist/editor/studioTheme.js +19 -16
  56. package/dist/editor/types.d.ts +1 -0
  57. package/dist/html/accessories.d.ts +7 -0
  58. package/dist/html/accessories.js +149 -0
  59. package/dist/html/motion.d.ts +1 -0
  60. package/dist/html/motion.js +189 -0
  61. package/dist/html/visemes.d.ts +7 -0
  62. package/dist/html/visemes.js +348 -0
  63. package/dist/html.d.ts +1 -1
  64. package/dist/html.js +56 -734
  65. package/dist/index.d.ts +19 -1
  66. package/dist/index.js +44 -5
  67. package/dist/index.web.d.ts +18 -1
  68. package/dist/index.web.js +36 -3
  69. package/dist/platform/api/types.d.ts +10 -0
  70. package/dist/platform/api/types.js +2 -0
  71. package/dist/platform/marketplace/types.d.ts +32 -0
  72. package/dist/platform/marketplace/types.js +2 -0
  73. package/dist/platform/sdk/unity.d.ts +27 -0
  74. package/dist/platform/sdk/unity.js +2 -0
  75. package/dist/platform/sdk/unreal.d.ts +23 -0
  76. package/dist/platform/sdk/unreal.js +2 -0
  77. package/dist/platform/sdk/web.d.ts +16 -0
  78. package/dist/platform/sdk/web.js +2 -0
  79. package/dist/sketchfab/api.js +5 -5
  80. package/dist/sketchfab/glbInspect.d.ts +22 -0
  81. package/dist/sketchfab/glbInspect.js +58 -0
  82. package/dist/sketchfab/index.d.ts +3 -0
  83. package/dist/sketchfab/index.js +8 -1
  84. package/dist/sketchfab/inspectRemote.d.ts +13 -0
  85. package/dist/sketchfab/inspectRemote.js +77 -0
  86. package/dist/sketchfab/types.d.ts +10 -0
  87. package/dist/sketchfab/useSketchfabSearch.js +1 -2
  88. package/dist/studio/AccessoryBrowserScreen.d.ts +6 -0
  89. package/dist/studio/AccessoryBrowserScreen.js +626 -0
  90. package/dist/studio/AccessoryPanel.d.ts +10 -0
  91. package/dist/studio/AccessoryPanel.js +396 -0
  92. package/dist/studio/AppearancePanel.d.ts +9 -0
  93. package/dist/studio/AppearancePanel.js +77 -0
  94. package/dist/studio/AvatarCreatorScreen.d.ts +5 -0
  95. package/dist/studio/AvatarCreatorScreen.js +806 -0
  96. package/dist/studio/AvatarEditorScreen.d.ts +14 -0
  97. package/dist/studio/AvatarEditorScreen.js +510 -0
  98. package/dist/studio/AvatarGrid.d.ts +23 -0
  99. package/dist/studio/AvatarGrid.js +257 -0
  100. package/dist/studio/ColorSwatch.d.ts +8 -0
  101. package/dist/studio/ColorSwatch.js +100 -0
  102. package/dist/studio/CreateVoiceProfileSheet.d.ts +8 -0
  103. package/dist/studio/CreateVoiceProfileSheet.js +242 -0
  104. package/dist/studio/DetailsPanel.d.ts +15 -0
  105. package/dist/studio/DetailsPanel.js +239 -0
  106. package/dist/studio/FilamentEditor.d.ts +2 -0
  107. package/dist/studio/FilamentEditor.js +6 -0
  108. package/dist/studio/PrecisionPanel.d.ts +2 -0
  109. package/dist/studio/PrecisionPanel.js +7 -0
  110. package/dist/studio/PublicGalleryScreen.d.ts +5 -0
  111. package/dist/studio/PublicGalleryScreen.js +358 -0
  112. package/dist/studio/SketchfabModelCard.d.ts +20 -0
  113. package/dist/studio/SketchfabModelCard.js +104 -0
  114. package/dist/studio/StudioBrowseHeader.d.ts +9 -0
  115. package/dist/studio/StudioBrowseHeader.js +28 -0
  116. package/dist/studio/StudioEmptyState.d.ts +8 -0
  117. package/dist/studio/StudioEmptyState.js +29 -0
  118. package/dist/studio/StudioFloatingAction.d.ts +13 -0
  119. package/dist/studio/StudioFloatingAction.js +42 -0
  120. package/dist/studio/StudioSectionHeader.d.ts +7 -0
  121. package/dist/studio/StudioSectionHeader.js +27 -0
  122. package/dist/studio/StudioSurfaceCard.d.ts +8 -0
  123. package/dist/studio/StudioSurfaceCard.js +20 -0
  124. package/dist/studio/VoicePanel.d.ts +15 -0
  125. package/dist/studio/VoicePanel.js +305 -0
  126. package/dist/studio/constants.d.ts +3 -0
  127. package/dist/studio/constants.js +6 -0
  128. package/dist/studio/index.d.ts +29 -0
  129. package/dist/studio/index.js +54 -0
  130. package/dist/studio/useSketchfabCapabilities.d.ts +31 -0
  131. package/dist/studio/useSketchfabCapabilities.js +82 -0
  132. package/dist/tts/useDirectVisemeStream.d.ts +2 -6
  133. package/dist/tts/useDirectVisemeStream.js +16 -12
  134. package/dist/tts/useMotionMarkers.d.ts +0 -1
  135. package/dist/tts/useMotionMarkers.js +1 -2
  136. package/dist/utils/avatarUtils.js +94 -8
  137. package/dist/utils/faceLandmarkerToShapeWeights.js +21 -14
  138. package/dist/voice/convertToWav.js +1 -2
  139. package/dist/voice/index.d.ts +3 -0
  140. package/dist/voice/index.js +6 -1
  141. package/dist/voice/useAudioPlayer.js +18 -6
  142. package/dist/voice/useAudioRecording.js +1 -2
  143. package/dist/voice/useFaceControls.d.ts +14 -0
  144. package/dist/voice/useFaceControls.js +81 -0
  145. package/dist/voice/useVoicePreview.d.ts +7 -0
  146. package/dist/voice/useVoicePreview.js +83 -0
  147. package/dist/wardrobe/index.d.ts +3 -0
  148. package/dist/wardrobe/index.js +8 -1
  149. package/dist/wardrobe/useAccessoryGestures.d.ts +20 -0
  150. package/dist/wardrobe/useAccessoryGestures.js +94 -0
  151. package/dist/wardrobe/useAvatarWardrobeHydration.js +9 -4
  152. package/dist/wardrobe/useStudioAvatar.d.ts +29 -0
  153. package/dist/wardrobe/useStudioAvatar.js +186 -0
  154. package/dist/wardrobe/wardrobeStore.d.ts +2 -0
  155. package/dist/wardrobe/wardrobeStore.js +12 -2
  156. package/dist/wgpu/R3FWebGpuCanvas.d.ts +15 -0
  157. package/dist/wgpu/R3FWebGpuCanvas.js +176 -0
  158. package/dist/wgpu/WgpuAvatar.d.ts +26 -2
  159. package/dist/wgpu/WgpuAvatar.js +313 -46
  160. package/dist/wgpu/accessoryDefaults.d.ts +12 -0
  161. package/dist/wgpu/accessoryDefaults.js +19 -0
  162. package/dist/wgpu/blobShim.d.ts +2 -0
  163. package/dist/wgpu/blobShim.js +191 -0
  164. package/dist/wgpu/index.d.ts +1 -0
  165. package/dist/wgpu/index.js +4 -1
  166. package/dist/wgpu/loadGLTFFromUri.d.ts +2 -0
  167. package/dist/wgpu/loadGLTFFromUri.js +75 -0
  168. package/dist/wgpu/morphTables.js +21 -10
  169. package/dist/wgpu/motionState.d.ts +20 -0
  170. package/dist/wgpu/motionState.js +31 -0
  171. package/dist/wgpu/patchThreeForRN.d.ts +28 -0
  172. package/dist/wgpu/patchThreeForRN.js +292 -0
  173. package/dist/wgpu/scenePlacement.d.ts +5 -0
  174. package/dist/wgpu/scenePlacement.js +50 -0
  175. package/dist/wgpu/useAuthedModelUri.js +22 -11
  176. package/dist/wgpu/useNativeGLTF.d.ts +7 -0
  177. package/dist/wgpu/useNativeGLTF.js +36 -0
  178. package/package.json +102 -32
@@ -15,15 +15,26 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.FaceSqueezeEditor = exports.FACE_SQUEEZE_LOCAL_MODULE = void 0;
36
+ exports.FACE_SQUEEZE_LOCAL_MODULE = void 0;
37
+ exports.FaceSqueezeEditor = FaceSqueezeEditor;
27
38
  const jsx_runtime_1 = require("react/jsx-runtime");
28
39
  /**
29
40
  * WgpuFaceSqueezeEditor — R3F/Three.js port of FaceSqueezeEditor.
@@ -37,18 +48,21 @@ const jsx_runtime_1 = require("react/jsx-runtime");
37
48
  * Drop-in replacement: same FaceSqueezeEditorProps interface.
38
49
  * The .web.tsx stub is shared — import from FaceSqueezeEditor.web.tsx.
39
50
  */
51
+ require("../wgpu/blobShim");
40
52
  const react_1 = require("react");
41
53
  const react_native_1 = require("react-native");
42
- const native_1 = require("@react-three/fiber/native");
43
- const native_2 = require("@react-three/drei/native");
54
+ const fiber_1 = require("@react-three/fiber");
44
55
  const THREE = __importStar(require("three"));
45
- const react_native_gesture_handler_1 = require("react-native-gesture-handler");
46
- const react_native_reanimated_1 = require("react-native-reanimated");
47
- const Haptics = __importStar(require("expo-haptics"));
48
- const expo_audio_1 = require("expo-audio");
49
- const expo_asset_1 = require("expo-asset");
56
+ const avatarUtils_1 = require("../utils/avatarUtils");
57
+ const R3FWebGpuCanvas_1 = require("../wgpu/R3FWebGpuCanvas");
58
+ const patchThreeForRN_1 = require("../wgpu/patchThreeForRN");
59
+ const useNativeGLTF_1 = require("../wgpu/useNativeGLTF");
50
60
  // eslint-disable-next-line @typescript-eslint/no-require-imports
51
61
  exports.FACE_SQUEEZE_LOCAL_MODULE = require('../assets/face-squeeze-local.glb');
62
+ (0, patchThreeForRN_1.installThreeRNPatches)(false);
63
+ function emitHaptic() {
64
+ // Optional in the example path.
65
+ }
52
66
  // ---------------------------------------------------------------------------
53
67
  // Constants (identical to Filament version)
54
68
  // ---------------------------------------------------------------------------
@@ -56,17 +70,6 @@ const SPRING_DURATION_MS = 300;
56
70
  const EMOTION_HOLD_MS = 900;
57
71
  const IDLE_EMOTION_INTERVAL_MS = 30000;
58
72
  const INITIAL_IDLE_EMOTION_DELAY_MS = 1200;
59
- // eslint-disable-next-line @typescript-eslint/no-require-imports
60
- const SOUND_OWIE = require('./sounds/owie.wav');
61
- // eslint-disable-next-line @typescript-eslint/no-require-imports
62
- const SOUND_STOP = require('./sounds/stop.wav');
63
- // eslint-disable-next-line @typescript-eslint/no-require-imports
64
- const SOUND_HAHA = require('./sounds/haha.wav');
65
- const REACTION_SOUNDS = [
66
- SOUND_OWIE,
67
- SOUND_STOP,
68
- SOUND_HAHA,
69
- ];
70
73
  const EMOTION_PRESETS = {
71
74
  owie: {
72
75
  eyeBlinkLeft: 1.0, eyeBlinkRight: 1.0,
@@ -147,12 +150,56 @@ function pickIdleEmotion() {
147
150
  }
148
151
  return IDLE_EMOTIONS[0].key;
149
152
  }
153
+ function morphAliases(name) {
154
+ const out = new Set([name, name.toLowerCase()]);
155
+ const pairs = [
156
+ ['Left', '_L'],
157
+ ['Right', '_R'],
158
+ ['Left', 'Left'],
159
+ ['Right', 'Right'],
160
+ ['left', '_l'],
161
+ ['right', '_r'],
162
+ ];
163
+ for (const [needle, replacement] of pairs) {
164
+ if (!name.includes(needle))
165
+ continue;
166
+ out.add(name.replace(needle, replacement));
167
+ out.add(name.replace(needle, replacement).toLowerCase());
168
+ out.add(name.replace(needle, replacement.replace('_', '')));
169
+ out.add(name.replace(needle, replacement.replace('_', '')).toLowerCase());
170
+ }
171
+ if (name.endsWith('Left')) {
172
+ const stem = name.slice(0, -4);
173
+ out.add(`${stem}_L`);
174
+ out.add(`${stem}_l`);
175
+ out.add(`${stem}L`);
176
+ out.add(`${stem}Left`);
177
+ }
178
+ if (name.endsWith('Right')) {
179
+ const stem = name.slice(0, -5);
180
+ out.add(`${stem}_R`);
181
+ out.add(`${stem}_r`);
182
+ out.add(`${stem}R`);
183
+ out.add(`${stem}Right`);
184
+ }
185
+ return [...out];
186
+ }
187
+ function resolveMorphIndices(index, name) {
188
+ const found = new Set();
189
+ for (const alias of morphAliases(name)) {
190
+ const i = index.get(alias) ?? index.get(alias.toLowerCase());
191
+ if (i !== undefined)
192
+ found.add(i);
193
+ }
194
+ return [...found];
195
+ }
150
196
  // ---------------------------------------------------------------------------
151
197
  // Inner R3F scene — runs inside Canvas
152
198
  // ---------------------------------------------------------------------------
153
199
  function AvatarScene({ uri, morphState, onReady, }) {
154
- const { camera } = (0, native_1.useThree)();
155
- const gltf = (0, native_2.useGLTF)(uri);
200
+ const { camera } = (0, fiber_1.useThree)();
201
+ const { gltf } = (0, useNativeGLTF_1.useNativeGLTF)(uri);
202
+ const scene = gltf?.scene ?? null;
156
203
  const readyFiredRef = (0, react_1.useRef)(false);
157
204
  // Position camera to match Filament version
158
205
  (0, react_1.useLayoutEffect)(() => {
@@ -165,11 +212,11 @@ function AvatarScene({ uri, morphState, onReady, }) {
165
212
  }, [camera]);
166
213
  // Find best mesh and build morph index
167
214
  (0, react_1.useEffect)(() => {
168
- if (!gltf?.scene)
215
+ if (!scene)
169
216
  return;
170
217
  let best = null;
171
218
  let bestCount = 0;
172
- gltf.scene.traverse((node) => {
219
+ scene.traverse((node) => {
173
220
  if (!(node instanceof THREE.Mesh))
174
221
  return;
175
222
  const count = Object.keys(node.morphTargetDictionary ?? {}).length;
@@ -192,33 +239,31 @@ function AvatarScene({ uri, morphState, onReady, }) {
192
239
  readyFiredRef.current = true;
193
240
  onReady();
194
241
  }
195
- }, [gltf, morphState, onReady]);
242
+ }, [scene, morphState, onReady]);
196
243
  // Apply morphState.weights to mesh every frame
197
- (0, native_1.useFrame)(() => {
244
+ (0, fiber_1.useFrame)(() => {
198
245
  const { mesh, weights, index } = morphState.current;
199
246
  if (!mesh?.morphTargetInfluences)
200
247
  return;
201
248
  for (const [name, w] of Object.entries(weights)) {
202
- const i = index.get(name) ?? index.get(name.toLowerCase());
203
- if (i !== undefined)
249
+ for (const i of resolveMorphIndices(index, name)) {
204
250
  mesh.morphTargetInfluences[i] = w;
251
+ }
205
252
  }
206
253
  });
207
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("ambientLight", { intensity: 0.65 }), (0, jsx_runtime_1.jsx)("directionalLight", { position: [1, 3, 2], intensity: 1.3 }), (0, jsx_runtime_1.jsx)("primitive", { object: gltf.scene })] }));
254
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("ambientLight", { intensity: 0.65 }), (0, jsx_runtime_1.jsx)("directionalLight", { position: [1, 3, 2], intensity: 1.3 }), scene ? (0, jsx_runtime_1.jsx)("primitive", { object: scene }) : null] }));
208
255
  }
209
256
  // ---------------------------------------------------------------------------
210
257
  // Main component
211
258
  // ---------------------------------------------------------------------------
212
- function FaceSqueezeEditor({ onClose }) {
259
+ function FaceSqueezeEditor({ onClose, avatarModule = exports.FACE_SQUEEZE_LOCAL_MODULE, }) {
213
260
  const [localUri, setLocalUri] = (0, react_1.useState)(null);
214
261
  const [isReady, setIsReady] = (0, react_1.useState)(false);
215
262
  (0, react_1.useEffect)(() => {
216
263
  let cancelled = false;
217
- (async () => {
264
+ void (async () => {
218
265
  try {
219
- const asset = expo_asset_1.Asset.fromModule(exports.FACE_SQUEEZE_LOCAL_MODULE);
220
- await asset.downloadAsync();
221
- const uri = asset.localUri ?? asset.uri;
266
+ const uri = await (0, avatarUtils_1.resolveLocalAssetUrl)(avatarModule);
222
267
  if (!cancelled && uri)
223
268
  setLocalUri(uri);
224
269
  }
@@ -227,16 +272,13 @@ function FaceSqueezeEditor({ onClose }) {
227
272
  }
228
273
  })();
229
274
  return () => { cancelled = true; };
230
- }, []);
275
+ }, [avatarModule]);
231
276
  // Shared morph state — mutated directly, never triggers re-render
232
277
  const morphState = (0, react_1.useRef)({
233
278
  weights: {},
234
279
  index: new Map(),
235
280
  mesh: null,
236
281
  });
237
- // ---- Audio ----
238
- const reactionSoundIdx = (0, react_1.useRef)(0);
239
- const player = (0, expo_audio_1.useAudioPlayer)(REACTION_SOUNDS[0]);
240
282
  // ---- Timers ----
241
283
  const springRef = (0, react_1.useRef)(null);
242
284
  const emotionHoldTimerRef = (0, react_1.useRef)(null);
@@ -244,19 +286,17 @@ function FaceSqueezeEditor({ onClose }) {
244
286
  const lipSyncIntervalRef = (0, react_1.useRef)(null);
245
287
  // ---- View geometry (for gesture zone math) ----
246
288
  const viewSize = (0, react_1.useRef)({ width: 1, height: 1 });
247
- const viewWidthSv = (0, react_native_reanimated_1.useSharedValue)(1);
248
- const viewHeightSv = (0, react_native_reanimated_1.useSharedValue)(1);
249
- const touchStartXSv = (0, react_native_reanimated_1.useSharedValue)(0);
250
- const touchStartYSv = (0, react_native_reanimated_1.useSharedValue)(0);
251
- const touchZoneSv = (0, react_native_reanimated_1.useSharedValue)('cheek_left');
252
- const lastPanUpdateTsSv = (0, react_native_reanimated_1.useSharedValue)(0);
289
+ const touchStartRef = (0, react_1.useRef)({ x: 0, y: 0 });
290
+ const touchZoneRef = (0, react_1.useRef)('cheek_left');
291
+ const lastPanUpdateTsRef = (0, react_1.useRef)(0);
292
+ const pinchStartDistanceRef = (0, react_1.useRef)(null);
293
+ const activeTouchesRef = (0, react_1.useRef)(0);
294
+ const hasMovedRef = (0, react_1.useRef)(false);
253
295
  const onLayout = (0, react_1.useCallback)((e) => {
254
296
  const w = Math.max(1, e.nativeEvent.layout.width);
255
297
  const h = Math.max(1, e.nativeEvent.layout.height);
256
298
  viewSize.current = { width: w, height: h };
257
- viewWidthSv.value = w;
258
- viewHeightSv.value = h;
259
- }, [viewHeightSv, viewWidthSv]);
299
+ }, []);
260
300
  // ---- Weight helpers ----
261
301
  const applyWeights = (0, react_1.useCallback)((targets) => {
262
302
  for (const [name, w] of Object.entries(targets)) {
@@ -340,15 +380,8 @@ function FaceSqueezeEditor({ onClose }) {
340
380
  }, TICK_MS);
341
381
  }, [applyWeights, stopLipSync]);
342
382
  const playReaction = (0, react_1.useCallback)((durationMs = 1400) => {
343
- const idx = reactionSoundIdx.current % REACTION_SOUNDS.length;
344
- reactionSoundIdx.current += 1;
345
- try {
346
- player.replace(REACTION_SOUNDS[idx]);
347
- player.play();
348
- }
349
- catch { /* ignore */ }
350
383
  startLipSync(durationMs);
351
- }, [player, startLipSync]);
384
+ }, [startLipSync]);
352
385
  // Cleanup
353
386
  (0, react_1.useEffect)(() => () => {
354
387
  if (springRef.current !== null)
@@ -373,10 +406,9 @@ function FaceSqueezeEditor({ onClose }) {
373
406
  };
374
407
  }, [isReady, playEmotionBurst]);
375
408
  // ---- Gesture callbacks (JS side — called via runOnJS) ----
376
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
377
409
  const onPanBeginJs = (0, react_1.useCallback)((_x, _y, _zone) => {
378
410
  stopSpring();
379
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
411
+ emitHaptic();
380
412
  }, [stopSpring]);
381
413
  const onPanUpdateJs = (0, react_1.useCallback)((zone, dx, dy, isLeftHalf) => {
382
414
  const u = {};
@@ -452,7 +484,7 @@ function FaceSqueezeEditor({ onClose }) {
452
484
  }, [springToZero]);
453
485
  const onPinchBeginJs = (0, react_1.useCallback)(() => {
454
486
  stopSpring();
455
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
487
+ emitHaptic();
456
488
  }, [stopSpring]);
457
489
  const onPinchUpdateJs = (0, react_1.useCallback)((scale, delta) => {
458
490
  if (scale < 1) {
@@ -471,83 +503,125 @@ function FaceSqueezeEditor({ onClose }) {
471
503
  const relY = y / Math.max(1, viewSize.current.height);
472
504
  if (relY < 0.16 || relY > 0.52)
473
505
  return;
474
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
506
+ emitHaptic();
475
507
  playReaction();
476
508
  playEmotionBurst('owie');
477
509
  }, [playEmotionBurst, playReaction]);
478
- // ---- Gestures ----
479
- const panGesture = react_native_gesture_handler_1.Gesture.Pan()
480
- .minDistance(0)
481
- .onBegin((e) => {
482
- 'worklet';
483
- touchStartXSv.value = e.x;
484
- touchStartYSv.value = e.y;
485
- const relY = e.y / Math.max(1, viewHeightSv.value);
486
- let zone;
510
+ const getZoneForPoint = (0, react_1.useCallback)((x, y) => {
511
+ const relY = y / Math.max(1, viewSize.current.height);
487
512
  if (relY < 0.20)
488
- zone = 'brow';
489
- else if (relY < 0.38)
490
- zone = 'eye';
491
- else if (relY > 0.72)
492
- zone = 'jaw';
493
- else
494
- zone = e.x < viewWidthSv.value / 2 ? 'cheek_left' : 'cheek_right';
495
- touchZoneSv.value = zone;
496
- (0, react_native_reanimated_1.runOnJS)(onPanBeginJs)(e.x, e.y, zone);
497
- })
498
- .onUpdate((e) => {
499
- 'worklet';
513
+ return 'brow';
514
+ if (relY < 0.38)
515
+ return 'eye';
516
+ if (relY > 0.72)
517
+ return 'jaw';
518
+ return x < viewSize.current.width / 2 ? 'cheek_left' : 'cheek_right';
519
+ }, []);
520
+ const distanceBetweenTouches = (0, react_1.useCallback)((event) => {
521
+ const touches = event.nativeEvent.touches;
522
+ if (touches.length < 2)
523
+ return null;
524
+ const [a, b] = [touches[0], touches[1]];
525
+ const dx = b.pageX - a.pageX;
526
+ const dy = b.pageY - a.pageY;
527
+ return Math.hypot(dx, dy);
528
+ }, []);
529
+ const handleResponderGrant = (0, react_1.useCallback)((event) => {
530
+ activeTouchesRef.current = event.nativeEvent.touches.length;
531
+ hasMovedRef.current = false;
532
+ if (event.nativeEvent.touches.length >= 2) {
533
+ pinchStartDistanceRef.current = distanceBetweenTouches(event);
534
+ onPinchBeginJs();
535
+ return;
536
+ }
537
+ const { locationX: x, locationY: y } = event.nativeEvent;
538
+ touchStartRef.current = { x, y };
539
+ touchZoneRef.current = getZoneForPoint(x, y);
540
+ lastPanUpdateTsRef.current = 0;
541
+ onPanBeginJs(x, y, touchZoneRef.current);
542
+ }, [distanceBetweenTouches, getZoneForPoint, onPanBeginJs, onPinchBeginJs]);
543
+ const handleResponderMove = (0, react_1.useCallback)((event) => {
544
+ const touchCount = event.nativeEvent.touches.length;
545
+ activeTouchesRef.current = touchCount;
546
+ if (touchCount >= 2) {
547
+ const currentDistance = distanceBetweenTouches(event);
548
+ const startDistance = pinchStartDistanceRef.current;
549
+ if (!currentDistance || !startDistance)
550
+ return;
551
+ hasMovedRef.current = true;
552
+ const scale = currentDistance / startDistance;
553
+ onPinchUpdateJs(scale, Math.abs(1 - scale));
554
+ return;
555
+ }
500
556
  const now = Date.now();
501
- if (now - lastPanUpdateTsSv.value < 33)
557
+ if (now - lastPanUpdateTsRef.current < 33)
502
558
  return;
503
- lastPanUpdateTsSv.value = now;
504
- const w = Math.max(1, viewWidthSv.value);
505
- const h = Math.max(1, viewHeightSv.value);
506
- const dx = (e.x - touchStartXSv.value) / w;
507
- const dy = (e.y - touchStartYSv.value) / h;
508
- (0, react_native_reanimated_1.runOnJS)(onPanUpdateJs)(touchZoneSv.value, dx, dy, touchStartXSv.value < w / 2);
509
- })
510
- .onEnd(() => {
511
- 'worklet';
512
- (0, react_native_reanimated_1.runOnJS)(onPanEndJs)();
513
- });
514
- const pinchGesture = react_native_gesture_handler_1.Gesture.Pinch()
515
- .onBegin(() => {
516
- 'worklet';
517
- (0, react_native_reanimated_1.runOnJS)(onPinchBeginJs)();
518
- })
519
- .onUpdate((e) => {
520
- 'worklet';
521
- (0, react_native_reanimated_1.runOnJS)(onPinchUpdateJs)(e.scale, Math.abs(1 - e.scale));
522
- })
523
- .onEnd(() => {
524
- 'worklet';
525
- (0, react_native_reanimated_1.runOnJS)(onPinchEndJs)();
526
- });
527
- const tapGesture = react_native_gesture_handler_1.Gesture.Tap()
528
- .onEnd((e, success) => {
529
- 'worklet';
530
- if (success)
531
- (0, react_native_reanimated_1.runOnJS)(onEyeTapJs)(e.x, e.y);
532
- });
533
- const composed = react_native_gesture_handler_1.Gesture.Simultaneous(panGesture, pinchGesture, tapGesture);
559
+ lastPanUpdateTsRef.current = now;
560
+ const { locationX: x, locationY: y } = event.nativeEvent;
561
+ const w = Math.max(1, viewSize.current.width);
562
+ const h = Math.max(1, viewSize.current.height);
563
+ const dx = (x - touchStartRef.current.x) / w;
564
+ const dy = (y - touchStartRef.current.y) / h;
565
+ if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01)
566
+ hasMovedRef.current = true;
567
+ onPanUpdateJs(touchZoneRef.current, dx, dy, touchStartRef.current.x < w / 2);
568
+ }, [distanceBetweenTouches, onPanUpdateJs, onPinchUpdateJs]);
569
+ const handleResponderRelease = (0, react_1.useCallback)((event) => {
570
+ if (activeTouchesRef.current >= 2 || pinchStartDistanceRef.current != null) {
571
+ pinchStartDistanceRef.current = null;
572
+ onPinchEndJs();
573
+ }
574
+ else {
575
+ onPanEndJs();
576
+ if (!hasMovedRef.current) {
577
+ const { locationX: x, locationY: y } = event.nativeEvent;
578
+ onEyeTapJs(x, y);
579
+ }
580
+ }
581
+ activeTouchesRef.current = 0;
582
+ hasMovedRef.current = false;
583
+ }, [onEyeTapJs, onPanEndJs, onPinchEndJs]);
584
+ const handleResponderTerminate = (0, react_1.useCallback)(() => {
585
+ if (pinchStartDistanceRef.current != null || activeTouchesRef.current >= 2) {
586
+ pinchStartDistanceRef.current = null;
587
+ onPinchEndJs();
588
+ }
589
+ else {
590
+ onPanEndJs();
591
+ }
592
+ activeTouchesRef.current = 0;
593
+ hasMovedRef.current = false;
594
+ }, [onPanEndJs, onPinchEndJs]);
534
595
  const handleReady = (0, react_1.useCallback)(() => setIsReady(true), []);
535
- return ((0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureHandlerRootView, { style: { flex: 1 }, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.scene, children: localUri ? ((0, jsx_runtime_1.jsx)(native_1.Canvas, { style: react_native_1.StyleSheet.absoluteFill, camera: { fov: 34, position: [0, 1.55, 1.05], near: 0.01, far: 50 }, gl: { antialias: true, alpha: false }, onCreated: ({ gl }) => gl.setClearColor(new THREE.Color('#0a0a0a')), children: (0, jsx_runtime_1.jsx)(AvatarScene, { uri: localUri, morphState: morphState, onReady: handleReady }) })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.loading, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.loadingText, children: "Loading face\u2026" }) })) }), (0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureDetector, { gesture: composed, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: react_native_1.StyleSheet.absoluteFill, onLayout: onLayout, collapsable: false }) }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.closeBtn, onPress: onClose, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.closeBtnText, children: "\u2715 Done squeezing" }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.hint, children: "Drag to stretch \u00B7 Pinch lips \u00B7 Poke an eye \uD83D\uDC41" })] }) }));
596
+ const editorCamera = (0, react_1.useMemo)(() => ({
597
+ fov: 34,
598
+ position: [0, 1.55, 1.05],
599
+ near: 0.01,
600
+ far: 50,
601
+ }), []);
602
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.scene, children: [localUri ? ((0, jsx_runtime_1.jsx)(R3FWebGpuCanvas_1.R3FWebGpuCanvas, { style: react_native_1.StyleSheet.absoluteFill, camera: editorCamera, clearColor: "#0a0a0a", children: (0, jsx_runtime_1.jsx)(AvatarScene, { uri: localUri, morphState: morphState, onReady: handleReady }) })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.loading, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.loadingText, children: "Loading face\u2026" }) })), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [react_native_1.StyleSheet.absoluteFill, styles.touchOverlay], pointerEvents: "box-only", onLayout: onLayout, collapsable: false, onStartShouldSetResponder: () => true, onStartShouldSetResponderCapture: () => true, onMoveShouldSetResponder: () => true, onMoveShouldSetResponderCapture: () => true, onResponderGrant: handleResponderGrant, onResponderMove: handleResponderMove, onResponderRelease: handleResponderRelease, onResponderTerminate: handleResponderTerminate, onResponderTerminationRequest: () => true })] }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.closeBtn, onPress: onClose, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.closeBtnText, children: "\u2715 Done squeezing" }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.hint, children: "Drag to stretch \u00B7 Pinch lips \u00B7 Poke an eye \uD83D\uDC41" })] }));
536
603
  }
537
- exports.FaceSqueezeEditor = FaceSqueezeEditor;
538
604
  const styles = react_native_1.StyleSheet.create({
539
605
  container: { flex: 1, backgroundColor: '#0a0a0a' },
540
- scene: { flex: 1 },
606
+ scene: { flex: 1, position: 'relative' },
607
+ touchOverlay: {
608
+ zIndex: 10,
609
+ elevation: 10,
610
+ backgroundColor: 'transparent',
611
+ },
541
612
  loading: { flex: 1, alignItems: 'center', justifyContent: 'center' },
542
613
  loadingText: { color: '#97a3b5', fontSize: 14 },
543
614
  closeBtn: {
544
615
  position: 'absolute', top: 52, right: 16,
545
616
  backgroundColor: '#6c63ff', borderRadius: 20,
546
617
  paddingHorizontal: 16, paddingVertical: 8,
618
+ zIndex: 20,
619
+ elevation: 20,
547
620
  },
548
621
  closeBtnText: { color: '#fff', fontWeight: '700', fontSize: 14 },
549
622
  hint: {
550
623
  position: 'absolute', bottom: 40, alignSelf: 'center',
551
624
  color: '#97a3b5', fontSize: 13,
625
+ zIndex: 20,
552
626
  },
553
627
  });
@@ -1,6 +1,8 @@
1
- export declare const FACE_SQUEEZE_LOCAL_MODULE: number;
1
+ /** Web builds do not bundle the native face-squeeze GLB module. */
2
+ export declare const FACE_SQUEEZE_LOCAL_MODULE: null;
2
3
  export interface FaceSqueezeEditorProps {
3
4
  onClose: () => void;
5
+ avatarModule?: unknown;
4
6
  }
5
7
  export declare function FaceSqueezeEditor({ onClose }: FaceSqueezeEditorProps): import("react/jsx-runtime").JSX.Element;
6
8
  export default FaceSqueezeEditor;
@@ -1,54 +1,56 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FaceSqueezeEditor = exports.FACE_SQUEEZE_LOCAL_MODULE = void 0;
3
+ exports.FACE_SQUEEZE_LOCAL_MODULE = void 0;
4
+ exports.FaceSqueezeEditor = FaceSqueezeEditor;
4
5
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_native_1 = require("react-native");
6
- // eslint-disable-next-line @typescript-eslint/no-require-imports
7
- exports.FACE_SQUEEZE_LOCAL_MODULE = require('../assets/face-squeeze-local.glb');
6
+ /** Web builds do not bundle the native face-squeeze GLB module. */
7
+ exports.FACE_SQUEEZE_LOCAL_MODULE = null;
8
8
  function FaceSqueezeEditor({ onClose }) {
9
- return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.container, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.card, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.title, children: "Face squeeze editor is mobile-only." }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.subtitle, children: "This placeholder keeps web builds safe while preserving native functionality." }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.closeButton, onPress: onClose, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.closeButtonText, children: "Close" }) })] }) }));
9
+ return ((0, jsx_runtime_1.jsx)("div", { style: styles.container, children: (0, jsx_runtime_1.jsxs)("div", { style: styles.card, children: [(0, jsx_runtime_1.jsx)("h2", { style: styles.title, children: "Face squeeze editor is mobile-only." }), (0, jsx_runtime_1.jsx)("p", { style: styles.subtitle, children: "This placeholder keeps web builds safe while preserving native functionality." }), (0, jsx_runtime_1.jsx)("button", { type: "button", style: styles.closeButton, onClick: onClose, children: "Close" })] }) }));
10
10
  }
11
- exports.FaceSqueezeEditor = FaceSqueezeEditor;
12
- const styles = react_native_1.StyleSheet.create({
11
+ const styles = {
13
12
  container: {
13
+ alignItems: 'center',
14
+ backgroundColor: '#0f1115',
15
+ boxSizing: 'border-box',
16
+ display: 'flex',
14
17
  flex: 1,
15
- alignItems: "center",
16
- justifyContent: "center",
17
- backgroundColor: "#0f1115",
18
+ height: '100%',
19
+ justifyContent: 'center',
18
20
  padding: 20,
21
+ width: '100%',
19
22
  },
20
23
  card: {
21
- width: "100%",
22
- maxWidth: 460,
24
+ backgroundColor: '#171b23',
25
+ border: '1px solid #2d3340',
23
26
  borderRadius: 12,
24
- borderWidth: 1,
25
- borderColor: "#2d3340",
26
- backgroundColor: "#171b23",
27
+ boxSizing: 'border-box',
28
+ maxWidth: 460,
27
29
  padding: 16,
28
- gap: 10,
30
+ width: '100%',
29
31
  },
30
32
  title: {
31
- color: "#f1f4f8",
33
+ color: '#f1f4f8',
32
34
  fontSize: 18,
33
- fontWeight: "700",
35
+ fontWeight: 700,
36
+ margin: 0,
34
37
  },
35
38
  subtitle: {
36
- color: "#98a1b3",
39
+ color: '#98a1b3',
37
40
  fontSize: 14,
38
- lineHeight: 20,
41
+ lineHeight: '20px',
42
+ margin: '10px 0 0',
39
43
  },
40
44
  closeButton: {
41
- marginTop: 6,
42
- alignSelf: "flex-start",
45
+ backgroundColor: '#2e8f5b',
46
+ border: 0,
43
47
  borderRadius: 8,
44
- backgroundColor: "#2e8f5b",
45
- paddingHorizontal: 12,
46
- paddingVertical: 8,
47
- },
48
- closeButtonText: {
49
- color: "#ffffff",
48
+ color: '#ffffff',
49
+ cursor: 'pointer',
50
50
  fontSize: 14,
51
- fontWeight: "600",
51
+ fontWeight: 600,
52
+ marginTop: 16,
53
+ padding: '8px 12px',
52
54
  },
53
- });
55
+ };
54
56
  exports.default = FaceSqueezeEditor;
@@ -1,15 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RigidAccessory = void 0;
3
+ exports.RigidAccessory = RigidAccessory;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  // @ts-nocheck
6
+ /* eslint-disable @typescript-eslint/no-explicit-any -- Accessory attachment walks arbitrary GLTF scene nodes from Three.js loader output. */
6
7
  const drei_1 = require("@react-three/drei");
7
8
  const react_1 = require("react");
8
9
  const three_1 = require("three");
9
10
  function RigidAccessory({ assetId, url, avatarScene, attachBone, offsetPosition, offsetRotation, scale = 1, isEditing = false, onPlacementChange, }) {
10
11
  const { scene } = (0, drei_1.useGLTF)(url);
11
- // Clone uniquely for this instance
12
+ // Clone uniquely for this instance — dispose geometry/materials on replacement or unmount
12
13
  const clone = (0, react_1.useMemo)(() => scene.clone(true), [scene]);
14
+ (0, react_1.useEffect)(() => {
15
+ return () => {
16
+ clone.traverse((obj) => {
17
+ if (obj instanceof three_1.Mesh) {
18
+ obj.geometry?.dispose();
19
+ if (Array.isArray(obj.material))
20
+ obj.material.forEach((m) => m.dispose());
21
+ else
22
+ obj.material?.dispose();
23
+ }
24
+ });
25
+ };
26
+ }, [clone]);
13
27
  const [basePos, setBasePos] = (0, react_1.useState)(null);
14
28
  (0, react_1.useEffect)(() => {
15
29
  if (!avatarScene)
@@ -44,7 +58,8 @@ function RigidAccessory({ assetId, url, avatarScene, attachBone, offsetPosition,
44
58
  clone.rotation.set(...offsetRotation);
45
59
  }
46
60
  if (scale !== undefined) {
47
- clone.scale.set(scale, scale, scale);
61
+ const s = Math.max(0.001, scale);
62
+ clone.scale.set(s, s, s);
48
63
  }
49
64
  }, [basePos, offsetPosition, offsetRotation, scale, clone]);
50
65
  const handleDragEnd = () => {
@@ -76,4 +91,3 @@ function RigidAccessory({ assetId, url, avatarScene, attachBone, offsetPosition,
76
91
  }
77
92
  return (0, jsx_runtime_1.jsx)("primitive", { object: clone });
78
93
  }
79
- exports.RigidAccessory = RigidAccessory;
@@ -15,17 +15,28 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.SkinnedClothing = void 0;
36
+ exports.SkinnedClothing = SkinnedClothing;
27
37
  const jsx_runtime_1 = require("react/jsx-runtime");
28
38
  // @ts-nocheck
39
+ /* eslint-disable @typescript-eslint/no-explicit-any -- SkeletonUtils.clone and GLTF skeleton rebinding expose untyped Three.js internals. */
29
40
  const drei_1 = require("@react-three/drei");
30
41
  const react_1 = require("react");
31
42
  const THREE = __importStar(require("three"));
@@ -113,4 +124,3 @@ function SkinnedClothing({ url, avatarSkeleton }) {
113
124
  }, [scene, avatarSkeleton]);
114
125
  return (0, jsx_runtime_1.jsx)("group", { ref: groupRef });
115
126
  }
116
- exports.SkinnedClothing = SkinnedClothing;