talking-head-studio 0.4.11 → 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 (142) hide show
  1. package/README.md +279 -193
  2. package/dist/TalkingHead.d.ts +28 -3
  3. package/dist/TalkingHead.js +21 -2
  4. package/dist/TalkingHead.web.d.ts +31 -4
  5. package/dist/TalkingHead.web.js +11 -1
  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 +16 -2
  10. package/dist/contract.d.ts +14 -0
  11. package/dist/contract.js +30 -0
  12. package/dist/core/avatar/avatarCapabilities.d.ts +60 -0
  13. package/dist/core/avatar/avatarCapabilities.js +100 -0
  14. package/dist/core/avatar/backends/gaussian.js +6 -4
  15. package/dist/core/avatar/motion.d.ts +1713 -0
  16. package/dist/core/avatar/motion.js +550 -0
  17. package/dist/core/avatar/motionRuntime.d.ts +46 -0
  18. package/dist/core/avatar/motionRuntime.js +84 -0
  19. package/dist/core/avatar/schema.d.ts +33 -5
  20. package/dist/core/avatar/visemes.d.ts +16 -1
  21. package/dist/core/avatar/visemes.js +48 -1
  22. package/dist/editor/AvatarCanvas.js +92 -1
  23. package/dist/editor/AvatarEditor.native.js +1 -0
  24. package/dist/editor/AvatarModel.js +1 -0
  25. package/dist/editor/FaceSqueezeEditor.d.ts +3 -1
  26. package/dist/editor/FaceSqueezeEditor.js +176 -112
  27. package/dist/editor/FaceSqueezeEditor.web.d.ts +3 -1
  28. package/dist/editor/FaceSqueezeEditor.web.js +30 -28
  29. package/dist/editor/RigidAccessory.js +17 -2
  30. package/dist/editor/SkinnedClothing.js +1 -0
  31. package/dist/editor/boneLockedDrag.d.ts +11 -0
  32. package/dist/editor/boneLockedDrag.js +68 -0
  33. package/dist/editor/boneSnap.web.d.ts +27 -0
  34. package/dist/editor/boneSnap.web.js +99 -0
  35. package/dist/editor/index.web.d.ts +10 -0
  36. package/dist/editor/index.web.js +26 -0
  37. package/dist/editor/sounds/haha.wav +0 -0
  38. package/dist/editor/sounds/owie.wav +0 -0
  39. package/dist/editor/sounds/stop.wav +0 -0
  40. package/dist/editor/studioTheme.d.ts +14 -14
  41. package/dist/editor/studioTheme.js +17 -14
  42. package/dist/editor/types.d.ts +1 -0
  43. package/dist/html/accessories.d.ts +7 -0
  44. package/dist/html/accessories.js +149 -0
  45. package/dist/html/motion.d.ts +1 -0
  46. package/dist/html/motion.js +189 -0
  47. package/dist/html/visemes.d.ts +7 -0
  48. package/dist/html/visemes.js +348 -0
  49. package/dist/html.d.ts +1 -1
  50. package/dist/html.js +55 -732
  51. package/dist/index.d.ts +7 -3
  52. package/dist/index.js +17 -1
  53. package/dist/index.web.d.ts +18 -1
  54. package/dist/index.web.js +36 -3
  55. package/dist/sketchfab/api.js +1 -0
  56. package/dist/sketchfab/glbInspect.d.ts +22 -0
  57. package/dist/sketchfab/glbInspect.js +58 -0
  58. package/dist/sketchfab/index.d.ts +3 -0
  59. package/dist/sketchfab/index.js +8 -1
  60. package/dist/sketchfab/inspectRemote.d.ts +13 -0
  61. package/dist/sketchfab/inspectRemote.js +77 -0
  62. package/dist/sketchfab/types.d.ts +10 -0
  63. package/dist/studio/AccessoryBrowserScreen.d.ts +6 -0
  64. package/dist/studio/AccessoryBrowserScreen.js +626 -0
  65. package/dist/studio/AccessoryPanel.d.ts +10 -0
  66. package/dist/studio/AccessoryPanel.js +396 -0
  67. package/dist/studio/AppearancePanel.d.ts +9 -0
  68. package/dist/studio/AppearancePanel.js +77 -0
  69. package/dist/studio/AvatarCreatorScreen.d.ts +5 -0
  70. package/dist/studio/AvatarCreatorScreen.js +806 -0
  71. package/dist/studio/AvatarEditorScreen.d.ts +14 -0
  72. package/dist/studio/AvatarEditorScreen.js +510 -0
  73. package/dist/studio/AvatarGrid.d.ts +23 -0
  74. package/dist/studio/AvatarGrid.js +257 -0
  75. package/dist/studio/ColorSwatch.d.ts +8 -0
  76. package/dist/studio/ColorSwatch.js +100 -0
  77. package/dist/studio/CreateVoiceProfileSheet.d.ts +8 -0
  78. package/dist/studio/CreateVoiceProfileSheet.js +242 -0
  79. package/dist/studio/DetailsPanel.d.ts +15 -0
  80. package/dist/studio/DetailsPanel.js +239 -0
  81. package/dist/studio/FilamentEditor.d.ts +2 -0
  82. package/dist/studio/FilamentEditor.js +6 -0
  83. package/dist/studio/PrecisionPanel.d.ts +2 -0
  84. package/dist/studio/PrecisionPanel.js +7 -0
  85. package/dist/studio/PublicGalleryScreen.d.ts +5 -0
  86. package/dist/studio/PublicGalleryScreen.js +358 -0
  87. package/dist/studio/SketchfabModelCard.d.ts +20 -0
  88. package/dist/studio/SketchfabModelCard.js +104 -0
  89. package/dist/studio/StudioBrowseHeader.d.ts +9 -0
  90. package/dist/studio/StudioBrowseHeader.js +28 -0
  91. package/dist/studio/StudioEmptyState.d.ts +8 -0
  92. package/dist/studio/StudioEmptyState.js +29 -0
  93. package/dist/studio/StudioFloatingAction.d.ts +13 -0
  94. package/dist/studio/StudioFloatingAction.js +42 -0
  95. package/dist/studio/StudioSectionHeader.d.ts +7 -0
  96. package/dist/studio/StudioSectionHeader.js +27 -0
  97. package/dist/studio/StudioSurfaceCard.d.ts +8 -0
  98. package/dist/studio/StudioSurfaceCard.js +20 -0
  99. package/dist/studio/VoicePanel.d.ts +15 -0
  100. package/dist/studio/VoicePanel.js +305 -0
  101. package/dist/studio/constants.d.ts +3 -0
  102. package/dist/studio/constants.js +6 -0
  103. package/dist/studio/index.d.ts +29 -0
  104. package/dist/studio/index.js +54 -0
  105. package/dist/studio/useSketchfabCapabilities.d.ts +31 -0
  106. package/dist/studio/useSketchfabCapabilities.js +82 -0
  107. package/dist/tts/useDirectVisemeStream.js +15 -10
  108. package/dist/utils/avatarUtils.js +92 -5
  109. package/dist/utils/faceLandmarkerToShapeWeights.js +2 -4
  110. package/dist/voice/useAudioPlayer.js +17 -4
  111. package/dist/voice/useVoicePreview.js +4 -2
  112. package/dist/wardrobe/index.d.ts +1 -0
  113. package/dist/wardrobe/index.js +6 -1
  114. package/dist/wardrobe/useAccessoryGestures.d.ts +20 -0
  115. package/dist/wardrobe/useAccessoryGestures.js +94 -0
  116. package/dist/wardrobe/useAvatarWardrobeHydration.js +8 -2
  117. package/dist/wardrobe/useStudioAvatar.js +11 -2
  118. package/dist/wardrobe/wardrobeStore.d.ts +2 -0
  119. package/dist/wardrobe/wardrobeStore.js +12 -2
  120. package/dist/wgpu/R3FWebGpuCanvas.d.ts +15 -0
  121. package/dist/wgpu/R3FWebGpuCanvas.js +176 -0
  122. package/dist/wgpu/WgpuAvatar.d.ts +26 -2
  123. package/dist/wgpu/WgpuAvatar.js +296 -39
  124. package/dist/wgpu/accessoryDefaults.d.ts +12 -0
  125. package/dist/wgpu/accessoryDefaults.js +19 -0
  126. package/dist/wgpu/blobShim.d.ts +2 -0
  127. package/dist/wgpu/blobShim.js +191 -0
  128. package/dist/wgpu/index.d.ts +1 -0
  129. package/dist/wgpu/index.js +4 -1
  130. package/dist/wgpu/loadGLTFFromUri.d.ts +2 -0
  131. package/dist/wgpu/loadGLTFFromUri.js +75 -0
  132. package/dist/wgpu/morphTables.js +21 -10
  133. package/dist/wgpu/motionState.d.ts +20 -0
  134. package/dist/wgpu/motionState.js +31 -0
  135. package/dist/wgpu/patchThreeForRN.d.ts +28 -0
  136. package/dist/wgpu/patchThreeForRN.js +292 -0
  137. package/dist/wgpu/scenePlacement.d.ts +5 -0
  138. package/dist/wgpu/scenePlacement.js +50 -0
  139. package/dist/wgpu/useAuthedModelUri.js +4 -2
  140. package/dist/wgpu/useNativeGLTF.d.ts +7 -0
  141. package/dist/wgpu/useNativeGLTF.js +36 -0
  142. package/package.json +97 -31
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
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
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DetailsPanel = void 0;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const react_1 = require("react");
39
+ const react_native_1 = require("react-native");
40
+ const Haptics = __importStar(require("expo-haptics"));
41
+ const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
42
+ const editor_1 = require("../editor");
43
+ const COLORS = {
44
+ background: editor_1.studioTheme.colors.surface,
45
+ card: editor_1.studioTheme.colors.surfaceRaised,
46
+ border: editor_1.studioTheme.colors.border,
47
+ accent: editor_1.studioTheme.colors.accent,
48
+ textMain: editor_1.studioTheme.colors.textPrimary,
49
+ textMuted: editor_1.studioTheme.colors.textMuted,
50
+ danger: editor_1.studioTheme.colors.danger,
51
+ inputBg: editor_1.studioTheme.colors.backgroundAlt
52
+ };
53
+ const CustomSwitch = ({ value, onValueChange }) => {
54
+ const offset = (0, react_native_reanimated_1.useSharedValue)(value ? 1 : 0);
55
+ (0, react_1.useEffect)(() => {
56
+ offset.value = (0, react_native_reanimated_1.withTiming)(value ? 1 : 0, { duration: 250 });
57
+ }, [offset, value]);
58
+ const animatedTrackStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
59
+ return {
60
+ backgroundColor: (0, react_native_reanimated_1.interpolateColor)(offset.value, [0, 1], [COLORS.inputBg, COLORS.accent])
61
+ };
62
+ });
63
+ const animatedThumbStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
64
+ return {
65
+ transform: [{ translateX: offset.value * 22 }]
66
+ };
67
+ });
68
+ return ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: () => onValueChange(!value), hitSlop: 10, style: styles.switchContainer, children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [styles.switchTrack, animatedTrackStyle], children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: [styles.switchThumb, animatedThumbStyle] }) }) }));
69
+ };
70
+ const DetailsPanel = ({ name, setName, description, setDescription, isPublic, setIsPublic, isDirty, saving, onSave, onDelete }) => {
71
+ const pulseOpacity = (0, react_native_reanimated_1.useSharedValue)(1);
72
+ const pulseScale = (0, react_native_reanimated_1.useSharedValue)(1);
73
+ (0, react_1.useEffect)(() => {
74
+ if (isDirty && !saving) {
75
+ pulseOpacity.value = (0, react_native_reanimated_1.withRepeat)((0, react_native_reanimated_1.withSequence)((0, react_native_reanimated_1.withTiming)(0.7, { duration: 1200 }), (0, react_native_reanimated_1.withTiming)(1, { duration: 1200 })), -1, true);
76
+ pulseScale.value = (0, react_native_reanimated_1.withRepeat)((0, react_native_reanimated_1.withSequence)((0, react_native_reanimated_1.withTiming)(0.98, { duration: 1200 }), (0, react_native_reanimated_1.withTiming)(1, { duration: 1200 })), -1, true);
77
+ }
78
+ else {
79
+ pulseOpacity.value = (0, react_native_reanimated_1.withTiming)(isDirty ? 1 : 0.4, { duration: 300 });
80
+ pulseScale.value = (0, react_native_reanimated_1.withTiming)(1, { duration: 300 });
81
+ }
82
+ }, [isDirty, pulseOpacity, pulseScale, saving]);
83
+ const animatedButtonStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
84
+ return {
85
+ opacity: pulseOpacity.value,
86
+ transform: [{ scale: pulseScale.value }]
87
+ };
88
+ });
89
+ const handleDelete = () => {
90
+ react_native_1.Alert.alert("Delete Avatar", "Are you sure you want to delete this avatar? This action cannot be undone.", [
91
+ { text: "Cancel", style: "cancel" },
92
+ { text: "Delete", style: "destructive", onPress: () => {
93
+ void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
94
+ onDelete();
95
+ } }
96
+ ]);
97
+ };
98
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.ScrollView, { style: styles.container, contentContainerStyle: styles.contentContainer, showsVerticalScrollIndicator: false, keyboardShouldPersistTaps: "handled", children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.card, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.section, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Name" }), (0, jsx_runtime_1.jsx)(react_native_1.TextInput, { style: styles.input, value: name, onChangeText: setName, placeholder: "Give your avatar a name", placeholderTextColor: COLORS.textMuted, selectionColor: COLORS.accent })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.section, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Description" }), (0, jsx_runtime_1.jsx)(react_native_1.TextInput, { style: [styles.input, styles.textArea], value: description, onChangeText: setDescription, placeholder: "Describe your avatar's persona and traits...", placeholderTextColor: COLORS.textMuted, selectionColor: COLORS.accent, multiline: true, textAlignVertical: "top" })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.section, styles.row, styles.lastSection], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.textContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Public Profile" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.subtext, children: "Allow others to view and interact with this avatar." })] }), (0, jsx_runtime_1.jsx)(CustomSwitch, { value: isPublic, onValueChange: setIsPublic })] })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.actionsContainer, children: (0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { style: animatedButtonStyle, children: (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [styles.saveButton, (!isDirty || saving) && styles.saveButtonDisabled], onPress: () => {
99
+ void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
100
+ onSave();
101
+ }, disabled: !isDirty || saving, children: saving ? ((0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { color: "#fff" })) : ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.saveButtonText, children: "Save Changes" })) }) }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.dangerZone, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.dangerTitle, children: "Danger Zone" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.dangerSubtext, children: "Permanently remove this avatar and all associated data." }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.deleteButton, onPress: handleDelete, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.deleteButtonText, children: "Delete Avatar" }) })] })] }));
102
+ };
103
+ exports.DetailsPanel = DetailsPanel;
104
+ exports.default = exports.DetailsPanel;
105
+ const styles = react_native_1.StyleSheet.create({
106
+ container: {
107
+ flex: 1,
108
+ backgroundColor: COLORS.background,
109
+ },
110
+ contentContainer: {
111
+ padding: 16,
112
+ paddingBottom: 40,
113
+ },
114
+ card: {
115
+ backgroundColor: COLORS.card,
116
+ borderRadius: 16,
117
+ borderWidth: 1,
118
+ borderColor: COLORS.border,
119
+ padding: 20,
120
+ marginBottom: 24,
121
+ },
122
+ section: {
123
+ marginBottom: 20,
124
+ },
125
+ lastSection: {
126
+ marginBottom: 0,
127
+ },
128
+ label: {
129
+ color: COLORS.textMain,
130
+ fontSize: 15,
131
+ fontWeight: '600',
132
+ marginBottom: 8,
133
+ letterSpacing: 0.3,
134
+ },
135
+ subtext: {
136
+ color: COLORS.textMuted,
137
+ fontSize: 13,
138
+ lineHeight: 18,
139
+ },
140
+ input: {
141
+ backgroundColor: COLORS.inputBg,
142
+ borderWidth: 1,
143
+ borderColor: COLORS.border,
144
+ borderRadius: 12,
145
+ color: COLORS.textMain,
146
+ fontSize: 15,
147
+ paddingHorizontal: 16,
148
+ paddingVertical: 14,
149
+ },
150
+ textArea: {
151
+ minHeight: 100,
152
+ paddingTop: 14,
153
+ },
154
+ row: {
155
+ flexDirection: 'row',
156
+ alignItems: 'center',
157
+ justifyContent: 'space-between',
158
+ },
159
+ textContainer: {
160
+ flex: 1,
161
+ paddingRight: 16,
162
+ },
163
+ switchContainer: {
164
+ justifyContent: 'center',
165
+ },
166
+ switchTrack: {
167
+ width: 48,
168
+ height: 26,
169
+ borderRadius: 13,
170
+ padding: 2,
171
+ },
172
+ switchThumb: {
173
+ width: 22,
174
+ height: 22,
175
+ backgroundColor: '#ffffff',
176
+ borderRadius: 11,
177
+ shadowColor: '#000',
178
+ shadowOffset: { width: 0, height: 2 },
179
+ shadowOpacity: 0.2,
180
+ shadowRadius: 2,
181
+ elevation: 2,
182
+ },
183
+ actionsContainer: {
184
+ marginBottom: 40,
185
+ },
186
+ saveButton: {
187
+ backgroundColor: COLORS.accent,
188
+ borderRadius: 999,
189
+ paddingVertical: 16,
190
+ alignItems: 'center',
191
+ justifyContent: 'center',
192
+ shadowColor: COLORS.accent,
193
+ shadowOffset: { width: 0, height: 4 },
194
+ shadowOpacity: 0.3,
195
+ shadowRadius: 8,
196
+ elevation: 4,
197
+ },
198
+ saveButtonDisabled: {
199
+ backgroundColor: COLORS.inputBg,
200
+ shadowOpacity: 0,
201
+ elevation: 0,
202
+ },
203
+ saveButtonText: {
204
+ color: '#ffffff',
205
+ fontSize: 16,
206
+ fontWeight: '600',
207
+ letterSpacing: 0.5,
208
+ },
209
+ dangerZone: {
210
+ marginTop: 'auto',
211
+ paddingTop: 24,
212
+ borderTopWidth: 1,
213
+ borderTopColor: COLORS.border,
214
+ },
215
+ dangerTitle: {
216
+ color: COLORS.danger,
217
+ fontSize: 15,
218
+ fontWeight: '600',
219
+ marginBottom: 4,
220
+ },
221
+ dangerSubtext: {
222
+ color: COLORS.textMuted,
223
+ fontSize: 13,
224
+ marginBottom: 16,
225
+ },
226
+ deleteButton: {
227
+ backgroundColor: 'transparent',
228
+ borderWidth: 1,
229
+ borderColor: 'rgba(240, 122, 122, 0.35)',
230
+ borderRadius: 18,
231
+ paddingVertical: 14,
232
+ alignItems: 'center',
233
+ },
234
+ deleteButtonText: {
235
+ color: COLORS.danger,
236
+ fontSize: 15,
237
+ fontWeight: '600',
238
+ },
239
+ });
@@ -0,0 +1,2 @@
1
+ export { AvatarEditor as FilamentEditor, AvatarEditor as default } from '../editor';
2
+ export type { AvatarEditorProps as FilamentEditorProps } from '../editor';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = exports.FilamentEditor = void 0;
4
+ var editor_1 = require("../editor");
5
+ Object.defineProperty(exports, "FilamentEditor", { enumerable: true, get: function () { return editor_1.AvatarEditor; } });
6
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return editor_1.AvatarEditor; } });
@@ -0,0 +1,2 @@
1
+ export declare function PrecisionPanel(_props: Record<string, unknown>): import("react/jsx-runtime").JSX.Element;
2
+ export default PrecisionPanel;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrecisionPanel = PrecisionPanel;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_native_1 = require("react-native");
6
+ function PrecisionPanel(_props) { return (0, jsx_runtime_1.jsx)(react_native_1.View, {}); }
7
+ exports.default = PrecisionPanel;
@@ -0,0 +1,5 @@
1
+ export interface PublicGalleryScreenProps {
2
+ onUseAvatar?: (fileUrl: string, avatarName: string) => void;
3
+ onNavigateToAssistant?: () => void;
4
+ }
5
+ export declare function PublicGalleryScreen({ onUseAvatar, onNavigateToAssistant, }: PublicGalleryScreenProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,358 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
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
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PublicGalleryScreen = PublicGalleryScreen;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const react_1 = __importStar(require("react"));
39
+ const react_native_1 = require("react-native");
40
+ const flash_list_1 = require("@shopify/flash-list");
41
+ const expo_image_1 = require("expo-image");
42
+ const moti_1 = require("moti");
43
+ const bottom_sheet_1 = __importStar(require("@gorhom/bottom-sheet"));
44
+ const Haptics = __importStar(require("expo-haptics"));
45
+ const expo_blur_1 = require("expo-blur");
46
+ const api_1 = require("../api");
47
+ const constants_1 = require("./constants");
48
+ const editor_1 = require("../editor");
49
+ const StudioBrowseHeader_1 = require("./StudioBrowseHeader");
50
+ const StudioEmptyState_1 = require("./StudioEmptyState");
51
+ const COLORS = editor_1.studioTheme.colors;
52
+ // ---------------------------------------------------------------------------
53
+ // Helpers
54
+ // ---------------------------------------------------------------------------
55
+ function truncateUserId(id, maxLen = 12) {
56
+ if (id.length <= maxLen)
57
+ return id;
58
+ return `${id.slice(0, maxLen)}\u2026`;
59
+ }
60
+ function formatVoiceBadge(avatar) {
61
+ if (!avatar.voice_name)
62
+ return null;
63
+ const lang = avatar.voice_language?.toUpperCase() ?? '';
64
+ return lang ? `${lang} \u2014 ${avatar.voice_name}` : avatar.voice_name;
65
+ }
66
+ const GalleryCard = react_1.default.memo(function GalleryCard({ avatar, index, onPress, headers, }) {
67
+ const thumbnailUrl = (0, api_1.avatarThumbnailUrl)(avatar);
68
+ const voiceBadge = formatVoiceBadge(avatar);
69
+ const delay = Math.min(index * 60, 600);
70
+ return ((0, jsx_runtime_1.jsx)(moti_1.MotiView, { from: { opacity: 0, translateY: 20 }, animate: { opacity: 1, translateY: 0 }, transition: { type: 'timing', duration: 400, delay }, style: styles.cardOuter, children: (0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { activeOpacity: 0.7, onPress: () => onPress(avatar), style: styles.card, children: [thumbnailUrl ? ((0, jsx_runtime_1.jsx)(expo_image_1.Image, { source: { uri: thumbnailUrl, headers }, style: styles.thumbnail, contentFit: "cover", placeholder: { blurhash: constants_1.BLURHASH }, transition: 250, cachePolicy: "memory-disk", recyclingKey: avatar.id })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.thumbnailPlaceholder, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.placeholderText, children: "3D" }) })), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.cardName, numberOfLines: 1, children: avatar.name }), voiceBadge ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.voiceBadge, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.voiceBadgeText, numberOfLines: 1, children: voiceBadge }) })) : null, (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.cardCreator, numberOfLines: 1, children: ["by ", truncateUserId(avatar.owner_user_id)] })] }) }));
71
+ });
72
+ // ---------------------------------------------------------------------------
73
+ // Backdrop component
74
+ // ---------------------------------------------------------------------------
75
+ const renderBackdrop = (props) => ((0, jsx_runtime_1.jsx)(bottom_sheet_1.BottomSheetBackdrop, { ...props, disappearsOnIndex: -1, appearsOnIndex: 0, pressBehavior: "close", opacity: 0.6 }));
76
+ // ---------------------------------------------------------------------------
77
+ // Main screen
78
+ // ---------------------------------------------------------------------------
79
+ function PublicGalleryScreen({ onUseAvatar, onNavigateToAssistant, }) {
80
+ const bottomSheetRef = (0, react_1.useRef)(null);
81
+ // State
82
+ const [avatars, setAvatars] = (0, react_1.useState)([]);
83
+ const [loading, setLoading] = (0, react_1.useState)(true);
84
+ const [error, setError] = (0, react_1.useState)(null);
85
+ const [headers, setHeaders] = (0, react_1.useState)({});
86
+ const [selected, setSelected] = (0, react_1.useState)(null);
87
+ // Bottom sheet snap points
88
+ const snapPoints = (0, react_1.useMemo)(() => ['50%', '75%'], []);
89
+ // ------ Data fetching ---------------------------------------------------
90
+ const loadData = (0, react_1.useCallback)(async () => {
91
+ try {
92
+ setLoading(true);
93
+ setError(null);
94
+ const [data, th] = await Promise.all([
95
+ (0, api_1.getPublicAvatars)(),
96
+ (0, api_1.thumbnailHeaders)(),
97
+ ]);
98
+ setAvatars(data);
99
+ setHeaders(th);
100
+ }
101
+ catch (err) {
102
+ setError(err instanceof Error ? err.message : 'Failed to load public avatars');
103
+ }
104
+ finally {
105
+ setLoading(false);
106
+ }
107
+ }, []);
108
+ (0, react_1.useEffect)(() => {
109
+ void loadData();
110
+ }, [loadData]);
111
+ // ------ Interactions ----------------------------------------------------
112
+ const handleCardPress = (0, react_1.useCallback)((avatar) => {
113
+ void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
114
+ setSelected(avatar);
115
+ bottomSheetRef.current?.snapToIndex(0);
116
+ }, []);
117
+ const handleUseAvatar = (0, react_1.useCallback)(() => {
118
+ if (!selected)
119
+ return;
120
+ void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
121
+ bottomSheetRef.current?.close();
122
+ const fileUrl = `${selected.id}/file`;
123
+ onUseAvatar?.(fileUrl, selected.name);
124
+ react_native_1.Alert.alert('Avatar Selected', `"${selected.name}" is ready. Go to your assistant to see it in action.`, [
125
+ { text: 'Stay Here', style: 'cancel' },
126
+ {
127
+ text: 'Go to Assistant',
128
+ onPress: () => onNavigateToAssistant?.(),
129
+ },
130
+ ]);
131
+ }, [selected, onUseAvatar, onNavigateToAssistant]);
132
+ const handleSheetChange = (0, react_1.useCallback)((index) => {
133
+ if (index === -1) {
134
+ setSelected(null);
135
+ }
136
+ }, []);
137
+ // ------ Render helpers --------------------------------------------------
138
+ const renderItem = (0, react_1.useCallback)(({ item, index }) => ((0, jsx_runtime_1.jsx)(GalleryCard, { avatar: item, index: index, onPress: handleCardPress, headers: headers })), [handleCardPress, headers]);
139
+ const keyExtractor = (0, react_1.useCallback)((item) => item.id, []);
140
+ // ------ Loading state ---------------------------------------------------
141
+ if (loading) {
142
+ 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.centered, children: [(0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { color: COLORS.accent, size: "large" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.loadingText, children: "Discovering avatars\\u2026" })] }) }));
143
+ }
144
+ // ------ Error state -----------------------------------------------------
145
+ if (error) {
146
+ 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.centered, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.errorText, children: error }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { onPress: loadData, style: styles.retryButton, activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.retryButtonText, children: "Retry" }) })] }) }));
147
+ }
148
+ // ------ Empty state -----------------------------------------------------
149
+ if (avatars.length === 0) {
150
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.container, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.centered, children: (0, jsx_runtime_1.jsx)(StudioEmptyState_1.StudioEmptyState, { icon: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.emptyIcon, children: "PUBLIC" }), title: "No public avatars yet", subtitle: "Be the first to share an avatar with the community." }) }) }));
151
+ }
152
+ // ------ Main grid -------------------------------------------------------
153
+ const selectedThumbnail = selected ? (0, api_1.avatarThumbnailUrl)(selected) : null;
154
+ const selectedVoice = selected ? formatVoiceBadge(selected) : null;
155
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
+ const AnyFlashList = flash_list_1.FlashList;
157
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsx)(AnyFlashList, { style: { flex: 1 }, data: avatars, keyExtractor: keyExtractor, renderItem: renderItem, numColumns: 2, estimatedItemSize: 200, contentContainerStyle: styles.gridContent, showsVerticalScrollIndicator: false, ListHeaderComponent: (0, jsx_runtime_1.jsx)(StudioBrowseHeader_1.StudioBrowseHeader, { eyebrow: "Gallery", title: "Public avatars", subtitle: "Browse community-ready characters and pull one straight into your assistant." }), ListFooterComponent: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.gridBottomSpacer }) }), (0, jsx_runtime_1.jsx)(bottom_sheet_1.default, { ref: bottomSheetRef, index: -1, snapPoints: snapPoints, enablePanDownToClose: true, onChange: handleSheetChange, backdropComponent: renderBackdrop, backgroundComponent: (props) => ((0, jsx_runtime_1.jsx)(expo_blur_1.BlurView, { ...props, tint: "systemThickMaterialDark", intensity: 90, style: [props.style, styles.sheetBackground, { backgroundColor: 'rgba(15, 19, 26, 0.4)' }] })), handleIndicatorStyle: styles.sheetHandle, children: (0, jsx_runtime_1.jsx)(bottom_sheet_1.BottomSheetView, { style: styles.sheetContent, children: selected ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedThumbnail ? ((0, jsx_runtime_1.jsx)(expo_image_1.Image, { source: { uri: selectedThumbnail, headers }, style: styles.sheetThumbnail, contentFit: "cover", placeholder: { blurhash: constants_1.BLURHASH }, transition: 300 })) : ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.sheetThumbnailPlaceholder, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.sheetPlaceholderText, children: "3D" }) })), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.sheetName, children: selected.name }), selected.description ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.sheetDescription, children: selected.description })) : null, (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.sheetMeta, children: [selectedVoice ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.sheetVoiceBadge, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.sheetVoiceBadgeText, children: selectedVoice }) })) : null, (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.sheetCreator, children: ["by ", truncateUserId(selected.owner_user_id, 20)] })] }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: styles.useButton, activeOpacity: 0.8, onPress: handleUseAvatar, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.useButtonText, children: "Use This Avatar" }) })] })) : null }) })] }));
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Styles
161
+ // ---------------------------------------------------------------------------
162
+ const styles = react_native_1.StyleSheet.create({
163
+ container: {
164
+ flex: 1,
165
+ backgroundColor: COLORS.background,
166
+ },
167
+ centered: {
168
+ flex: 1,
169
+ alignItems: 'center',
170
+ justifyContent: 'center',
171
+ paddingHorizontal: 32,
172
+ gap: 8,
173
+ },
174
+ gridContent: {
175
+ paddingHorizontal: 12,
176
+ },
177
+ gridBottomSpacer: {
178
+ height: 32,
179
+ },
180
+ cardOuter: {
181
+ flex: 1,
182
+ marginHorizontal: 6,
183
+ marginBottom: 12,
184
+ },
185
+ card: {
186
+ borderRadius: 16,
187
+ borderWidth: 1,
188
+ borderColor: COLORS.border,
189
+ backgroundColor: COLORS.surfaceRaised,
190
+ overflow: 'hidden',
191
+ padding: 10,
192
+ gap: 6,
193
+ shadowColor: '#000',
194
+ shadowOffset: { width: 0, height: 2 },
195
+ shadowOpacity: 0.25,
196
+ shadowRadius: 4,
197
+ elevation: 4,
198
+ },
199
+ thumbnail: {
200
+ width: '100%',
201
+ aspectRatio: 1,
202
+ borderRadius: 12,
203
+ backgroundColor: COLORS.backgroundAlt,
204
+ },
205
+ thumbnailPlaceholder: {
206
+ width: '100%',
207
+ aspectRatio: 1,
208
+ borderRadius: 12,
209
+ backgroundColor: COLORS.surfaceSoft,
210
+ alignItems: 'center',
211
+ justifyContent: 'center',
212
+ },
213
+ placeholderText: {
214
+ color: COLORS.textMuted,
215
+ fontSize: 22,
216
+ fontWeight: '700',
217
+ letterSpacing: 2,
218
+ },
219
+ cardName: {
220
+ color: COLORS.textPrimary,
221
+ fontSize: 13,
222
+ fontWeight: '600',
223
+ textAlign: 'center',
224
+ marginTop: 2,
225
+ },
226
+ voiceBadge: {
227
+ alignSelf: 'center',
228
+ paddingHorizontal: 8,
229
+ paddingVertical: 3,
230
+ borderRadius: 8,
231
+ backgroundColor: 'rgba(255, 122, 89, 0.14)',
232
+ },
233
+ voiceBadgeText: {
234
+ color: COLORS.accent,
235
+ fontSize: 10,
236
+ fontWeight: '600',
237
+ },
238
+ cardCreator: {
239
+ color: COLORS.textMuted,
240
+ fontSize: 10,
241
+ textAlign: 'center',
242
+ },
243
+ loadingText: {
244
+ color: COLORS.textSecondary,
245
+ fontSize: 13,
246
+ marginTop: 8,
247
+ },
248
+ errorText: {
249
+ color: COLORS.danger,
250
+ fontSize: 14,
251
+ fontWeight: '500',
252
+ textAlign: 'center',
253
+ marginBottom: 8,
254
+ },
255
+ retryButton: {
256
+ paddingHorizontal: 24,
257
+ paddingVertical: 10,
258
+ backgroundColor: COLORS.surfaceSoft,
259
+ borderRadius: 10,
260
+ borderWidth: 1,
261
+ borderColor: COLORS.border,
262
+ },
263
+ retryButtonText: {
264
+ color: COLORS.textPrimary,
265
+ fontSize: 13,
266
+ fontWeight: '600',
267
+ },
268
+ emptyIcon: {
269
+ fontSize: 40,
270
+ marginBottom: 4,
271
+ },
272
+ sheetBackground: {
273
+ backgroundColor: COLORS.surface,
274
+ borderTopLeftRadius: 24,
275
+ borderTopRightRadius: 24,
276
+ },
277
+ sheetHandle: {
278
+ backgroundColor: COLORS.border,
279
+ width: 40,
280
+ },
281
+ sheetContent: {
282
+ flex: 1,
283
+ paddingHorizontal: 24,
284
+ paddingTop: 8,
285
+ paddingBottom: 32,
286
+ gap: 12,
287
+ },
288
+ sheetThumbnail: {
289
+ width: '100%',
290
+ aspectRatio: 4 / 3,
291
+ borderRadius: 16,
292
+ backgroundColor: COLORS.backgroundAlt,
293
+ },
294
+ sheetThumbnailPlaceholder: {
295
+ width: '100%',
296
+ aspectRatio: 4 / 3,
297
+ borderRadius: 16,
298
+ backgroundColor: COLORS.surfaceSoft,
299
+ alignItems: 'center',
300
+ justifyContent: 'center',
301
+ },
302
+ sheetPlaceholderText: {
303
+ color: COLORS.textMuted,
304
+ fontSize: 32,
305
+ fontWeight: '700',
306
+ letterSpacing: 3,
307
+ },
308
+ sheetName: {
309
+ color: COLORS.textPrimary,
310
+ fontSize: 22,
311
+ fontWeight: '700',
312
+ letterSpacing: -0.3,
313
+ },
314
+ sheetDescription: {
315
+ color: COLORS.textSecondary,
316
+ fontSize: 14,
317
+ lineHeight: 20,
318
+ },
319
+ sheetMeta: {
320
+ flexDirection: 'row',
321
+ alignItems: 'center',
322
+ gap: 10,
323
+ flexWrap: 'wrap',
324
+ },
325
+ sheetVoiceBadge: {
326
+ paddingHorizontal: 10,
327
+ paddingVertical: 5,
328
+ borderRadius: 8,
329
+ backgroundColor: 'rgba(255, 122, 89, 0.14)',
330
+ },
331
+ sheetVoiceBadgeText: {
332
+ color: COLORS.accent,
333
+ fontSize: 12,
334
+ fontWeight: '600',
335
+ },
336
+ sheetCreator: {
337
+ color: COLORS.textMuted,
338
+ fontSize: 12,
339
+ },
340
+ useButton: {
341
+ marginTop: 8,
342
+ backgroundColor: COLORS.accent,
343
+ borderRadius: 14,
344
+ paddingVertical: 16,
345
+ alignItems: 'center',
346
+ shadowColor: COLORS.accent,
347
+ shadowOffset: { width: 0, height: 4 },
348
+ shadowOpacity: 0.4,
349
+ shadowRadius: 12,
350
+ elevation: 8,
351
+ },
352
+ useButtonText: {
353
+ color: '#fff',
354
+ fontSize: 16,
355
+ fontWeight: '700',
356
+ letterSpacing: 0.3,
357
+ },
358
+ });
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import type { SketchfabModel } from '../sketchfab';
3
+ /**
4
+ * Pre-flight avatar capability, surfaced as a card badge:
5
+ * - 'ready' lip-sync + body motion (or backend-verified)
6
+ * - 'lipsync' facial blend shapes, but the rig can't drive body motion
7
+ * - 'limited' inspected, can't lip-sync — sinks in good-first ranking
8
+ * - 'checking' inspection in flight
9
+ */
10
+ export type CapabilityBadge = 'ready' | 'lipsync' | 'limited' | 'checking';
11
+ interface SketchfabModelCardProps {
12
+ model: SketchfabModel;
13
+ index: number;
14
+ onPress: (model: SketchfabModel) => void;
15
+ showHumanoidBadge?: boolean;
16
+ /** Pre-flight capability badge from background GLB inspection. */
17
+ capability?: CapabilityBadge | null;
18
+ }
19
+ export declare const SketchfabModelCard: React.MemoExoticComponent<({ model, index, onPress, showHumanoidBadge, capability, }: SketchfabModelCardProps) => import("react/jsx-runtime").JSX.Element>;
20
+ export default SketchfabModelCard;