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
@@ -0,0 +1,257 @@
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.AvatarGrid = AvatarGrid;
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 vector_icons_1 = require("@expo/vector-icons");
41
+ const flash_list_1 = require("@shopify/flash-list");
42
+ const expo_image_1 = require("expo-image");
43
+ const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
44
+ const StudioEmptyState_1 = require("./StudioEmptyState");
45
+ const editor_1 = require("../editor");
46
+ // ---------------------------------------------------------------------------
47
+ // Constants
48
+ // ---------------------------------------------------------------------------
49
+ const MAX_STAGGER_DELAY = 500;
50
+ const STAGGER_STEP = 50;
51
+ function AvatarCard({ item, index, onPress, onDelete, thumbnailHeaders }) {
52
+ const delay = Math.min(index * STAGGER_STEP, MAX_STAGGER_DELAY);
53
+ const handleDelete = () => {
54
+ if (onDelete) {
55
+ react_native_1.Alert.alert("Delete Avatar", `Are you sure you want to delete "${item.name}"? This action cannot be undone.`, [
56
+ { text: "Cancel", style: "cancel" },
57
+ {
58
+ text: "Delete",
59
+ style: "destructive",
60
+ onPress: () => onDelete(item.id)
61
+ }
62
+ ]);
63
+ }
64
+ };
65
+ return ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { entering: react_native_reanimated_1.FadeInDown.delay(delay), style: [
66
+ styles.card,
67
+ item.isActive && styles.cardActive,
68
+ ], children: (0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { activeOpacity: 0.7, onPress: () => onPress(item.id), style: styles.cardTouchable, children: [item.thumbnailUrl ? ((0, jsx_runtime_1.jsx)(expo_image_1.Image, { source: {
69
+ uri: item.thumbnailUrl,
70
+ headers: thumbnailHeaders,
71
+ }, style: styles.thumbnail, contentFit: "cover", placeholder: { blurhash: 'L6PZfSi_.AyE_3t7t7R**0o#DgR4' }, transition: 200, cachePolicy: "memory-disk", recyclingKey: item.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" }) })), onDelete && ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: styles.deleteButton, onPress: handleDelete, hitSlop: 10, children: (0, jsx_runtime_1.jsx)(vector_icons_1.Ionicons, { name: "trash", size: 16, color: COLORS.error }) })), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.name, numberOfLines: 1, children: item.name }), item.subtitle ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.subtitle, numberOfLines: 1, children: item.subtitle })) : null, item.isActive ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.activeBadge, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.activeBadgeText, children: "Active" }) })) : null] }) }));
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Grid
75
+ // ---------------------------------------------------------------------------
76
+ function AvatarGrid({ items, loading, error, onSelect, onDelete, onRetry, emptyMessage = 'No avatars yet', emptySubMessage = 'Create one to get started.', thumbnailHeaders, ListHeaderComponent, bottomClearance = 120, }) {
77
+ const renderItem = (0, react_1.useCallback)(({ item, index }) => ((0, jsx_runtime_1.jsx)(AvatarCard, { item: item, index: index, onPress: onSelect, onDelete: onDelete, thumbnailHeaders: thumbnailHeaders })), [onSelect, onDelete, thumbnailHeaders]);
78
+ const keyExtractor = (0, react_1.useCallback)((item) => item.id, []);
79
+ // ---- Loading state -------------------------------------------------
80
+ if (loading) {
81
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.centeredContainer, children: [ListHeaderComponent ?? null, (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: "Loading avatars..." })] })] }));
82
+ }
83
+ // ---- Error state ---------------------------------------------------
84
+ if (error) {
85
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.centeredContainer, children: [ListHeaderComponent ?? null, (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 }), onRetry ? ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { onPress: onRetry, style: styles.retryButton, activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.retryText, children: "Retry" }) })) : null] })] }));
86
+ }
87
+ // ---- Empty state ---------------------------------------------------
88
+ if (items.length === 0) {
89
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.centeredContainer, children: [ListHeaderComponent ?? null, (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: "CAST" }), title: emptyMessage, subtitle: emptySubMessage }) })] }));
90
+ }
91
+ // ---- Grid ----------------------------------------------------------
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ const AnyFlashList = flash_list_1.FlashList;
94
+ return ((0, jsx_runtime_1.jsx)(AnyFlashList, { data: items, keyExtractor: keyExtractor, renderItem: renderItem, numColumns: 2, estimatedItemSize: 180, contentContainerStyle: [styles.grid, { paddingBottom: bottomClearance }], showsVerticalScrollIndicator: false, ListHeaderComponent: ListHeaderComponent }));
95
+ }
96
+ exports.default = AvatarGrid;
97
+ // ---------------------------------------------------------------------------
98
+ // Design tokens
99
+ // ---------------------------------------------------------------------------
100
+ const COLORS = {
101
+ background: editor_1.studioTheme.colors.background,
102
+ card: editor_1.studioTheme.colors.surfaceRaised,
103
+ border: editor_1.studioTheme.colors.border,
104
+ activeBorder: editor_1.studioTheme.colors.accent,
105
+ activeBg: editor_1.studioTheme.colors.accentMuted,
106
+ textPrimary: editor_1.studioTheme.colors.textPrimary,
107
+ textSecondary: editor_1.studioTheme.colors.textSecondary,
108
+ textMuted: editor_1.studioTheme.colors.textMuted,
109
+ error: editor_1.studioTheme.colors.danger,
110
+ accent: editor_1.studioTheme.colors.accent,
111
+ thumbnailBg: editor_1.studioTheme.colors.backgroundAlt,
112
+ placeholderBg: editor_1.studioTheme.colors.surfaceSoft,
113
+ };
114
+ // ---------------------------------------------------------------------------
115
+ // Styles
116
+ // ---------------------------------------------------------------------------
117
+ const styles = react_native_1.StyleSheet.create({
118
+ // Layout ---------------------------------------------------------------
119
+ grid: {
120
+ paddingHorizontal: 14,
121
+ paddingTop: 2,
122
+ },
123
+ // Card -----------------------------------------------------------------
124
+ card: {
125
+ flex: 1,
126
+ borderRadius: 18,
127
+ borderWidth: 1,
128
+ borderColor: COLORS.border,
129
+ backgroundColor: COLORS.card,
130
+ overflow: 'hidden',
131
+ // Margins replace FlatList columnWrapperStyle (gap: 12, marginBottom: 12)
132
+ marginHorizontal: 6,
133
+ marginBottom: 12,
134
+ // Shadow (iOS + Android elevation)
135
+ shadowColor: '#000',
136
+ shadowOffset: { width: 0, height: 8 },
137
+ shadowOpacity: 0.18,
138
+ shadowRadius: 16,
139
+ elevation: 6,
140
+ },
141
+ cardActive: {
142
+ borderColor: COLORS.activeBorder,
143
+ backgroundColor: COLORS.activeBg,
144
+ shadowColor: COLORS.activeBorder,
145
+ shadowOffset: { width: 0, height: 0 },
146
+ shadowOpacity: 0.24,
147
+ shadowRadius: 12,
148
+ elevation: 8,
149
+ },
150
+ cardTouchable: {
151
+ padding: 10,
152
+ gap: 6,
153
+ },
154
+ deleteButton: {
155
+ position: 'absolute',
156
+ top: 14,
157
+ right: 14,
158
+ backgroundColor: COLORS.card,
159
+ borderRadius: 12,
160
+ padding: 6,
161
+ borderWidth: 1,
162
+ borderColor: COLORS.border,
163
+ zIndex: 10,
164
+ },
165
+ // Thumbnail ------------------------------------------------------------
166
+ thumbnail: {
167
+ width: '100%',
168
+ aspectRatio: 1,
169
+ borderRadius: 12,
170
+ backgroundColor: COLORS.thumbnailBg,
171
+ },
172
+ thumbnailPlaceholder: {
173
+ width: '100%',
174
+ aspectRatio: 1,
175
+ borderRadius: 12,
176
+ backgroundColor: COLORS.placeholderBg,
177
+ alignItems: 'center',
178
+ justifyContent: 'center',
179
+ },
180
+ placeholderText: {
181
+ color: COLORS.textMuted,
182
+ fontSize: 18,
183
+ fontWeight: '700',
184
+ letterSpacing: 1.6,
185
+ },
186
+ // Text -----------------------------------------------------------------
187
+ name: {
188
+ color: COLORS.textPrimary,
189
+ fontSize: 14,
190
+ fontWeight: '700',
191
+ textAlign: 'center',
192
+ },
193
+ subtitle: {
194
+ color: COLORS.textSecondary,
195
+ fontSize: 12,
196
+ textAlign: 'center',
197
+ },
198
+ // Active badge ---------------------------------------------------------
199
+ activeBadge: {
200
+ alignSelf: 'center',
201
+ marginTop: 2,
202
+ paddingHorizontal: 10,
203
+ paddingVertical: 4,
204
+ borderRadius: 999,
205
+ backgroundColor: `${COLORS.accent}24`,
206
+ },
207
+ activeBadgeText: {
208
+ color: COLORS.accent,
209
+ fontSize: 10,
210
+ fontWeight: '700',
211
+ },
212
+ // Centered states (loading / error / empty) ----------------------------
213
+ centeredContainer: {
214
+ flex: 1,
215
+ backgroundColor: COLORS.background,
216
+ },
217
+ centered: {
218
+ flex: 1,
219
+ alignItems: 'center',
220
+ justifyContent: 'center',
221
+ paddingHorizontal: 32,
222
+ gap: 10,
223
+ },
224
+ loadingText: {
225
+ color: COLORS.textSecondary,
226
+ fontSize: 14,
227
+ marginTop: 8,
228
+ },
229
+ // Error ----------------------------------------------------------------
230
+ errorText: {
231
+ color: COLORS.error,
232
+ fontSize: 14,
233
+ fontWeight: '500',
234
+ textAlign: 'center',
235
+ marginBottom: 8,
236
+ },
237
+ retryButton: {
238
+ paddingHorizontal: 20,
239
+ paddingVertical: 10,
240
+ backgroundColor: COLORS.card,
241
+ borderRadius: 999,
242
+ borderWidth: 1,
243
+ borderColor: COLORS.border,
244
+ },
245
+ retryText: {
246
+ color: COLORS.textPrimary,
247
+ fontSize: 13,
248
+ fontWeight: '600',
249
+ },
250
+ // Empty ----------------------------------------------------------------
251
+ emptyIcon: {
252
+ color: COLORS.textSecondary,
253
+ fontSize: 14,
254
+ fontWeight: '800',
255
+ letterSpacing: 2.4,
256
+ },
257
+ });
@@ -0,0 +1,8 @@
1
+ export interface ColorSwatchProps {
2
+ color: string;
3
+ selected: boolean;
4
+ onPress: () => void;
5
+ label?: string;
6
+ size?: number;
7
+ }
8
+ export default function ColorSwatch({ color, selected, onPress, label, size, }: ColorSwatchProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,100 @@
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.default = ColorSwatch;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const react_native_1 = require("react-native");
39
+ const Haptics = __importStar(require("expo-haptics"));
40
+ const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
41
+ const editor_1 = require("../editor");
42
+ const AnimatedPressable = react_native_reanimated_1.default.createAnimatedComponent(react_native_1.Pressable);
43
+ const SPRING_CONFIG = {
44
+ damping: 12,
45
+ stiffness: 180,
46
+ mass: 0.8,
47
+ };
48
+ function ColorSwatch({ color, selected, onPress, label, size = 40, }) {
49
+ const scale = (0, react_native_reanimated_1.useSharedValue)(1);
50
+ const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
51
+ transform: [{ scale: scale.value }],
52
+ }));
53
+ const handlePressIn = () => {
54
+ scale.value = (0, react_native_reanimated_1.withSpring)(0.85, SPRING_CONFIG);
55
+ };
56
+ const handlePressOut = () => {
57
+ scale.value = (0, react_native_reanimated_1.withSpring)(1, SPRING_CONFIG);
58
+ };
59
+ const glowColor = `${color}99`; // ~60% opacity
60
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.wrapper, children: [(0, jsx_runtime_1.jsx)(AnimatedPressable, { onPress: () => {
61
+ void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
62
+ onPress();
63
+ }, onPressIn: handlePressIn, onPressOut: handlePressOut, style: [
64
+ animatedStyle,
65
+ styles.swatch,
66
+ {
67
+ width: size,
68
+ height: size,
69
+ borderRadius: size / 2,
70
+ backgroundColor: color,
71
+ },
72
+ selected
73
+ ? { borderWidth: 2, borderColor: glowColor }
74
+ : { borderWidth: 1, borderColor: editor_1.studioTheme.colors.border },
75
+ ], children: selected && ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
76
+ styles.check,
77
+ { fontSize: size * 0.45, lineHeight: size * 0.55 },
78
+ ], children: "\u2713" })) }), label != null && ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, numberOfLines: 1, children: label }))] }));
79
+ }
80
+ const styles = react_native_1.StyleSheet.create({
81
+ wrapper: {
82
+ alignItems: 'center',
83
+ gap: 4,
84
+ },
85
+ swatch: {
86
+ justifyContent: 'center',
87
+ alignItems: 'center',
88
+ },
89
+ check: {
90
+ color: '#ffffff',
91
+ fontWeight: '700',
92
+ textAlign: 'center',
93
+ },
94
+ label: {
95
+ color: editor_1.studioTheme.colors.textSecondary,
96
+ fontSize: 10,
97
+ textAlign: 'center',
98
+ maxWidth: 56,
99
+ },
100
+ });
@@ -0,0 +1,8 @@
1
+ import BottomSheet from '@gorhom/bottom-sheet';
2
+ import type React from 'react';
3
+ interface Props {
4
+ sheetRef: React.RefObject<BottomSheet | null>;
5
+ onSuccess: () => void;
6
+ }
7
+ export default function CreateVoiceProfileSheet({ sheetRef, onSuccess }: Props): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,242 @@
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.default = CreateVoiceProfileSheet;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const vector_icons_1 = require("@expo/vector-icons");
39
+ const bottom_sheet_1 = __importStar(require("@gorhom/bottom-sheet"));
40
+ const expo_audio_1 = require("expo-audio");
41
+ const DocumentPicker = __importStar(require("expo-document-picker"));
42
+ const react_1 = require("react");
43
+ const react_native_1 = require("react-native");
44
+ const api_1 = require("../api");
45
+ function CreateVoiceProfileSheet({ sheetRef, onSuccess }) {
46
+ const [name, setName] = (0, react_1.useState)('');
47
+ const [referenceText, setReferenceText] = (0, react_1.useState)('');
48
+ const [isSubmitting, setIsSubmitting] = (0, react_1.useState)(false);
49
+ const [audioUri, setAudioUri] = (0, react_1.useState)(null);
50
+ const [audioName, setAudioName] = (0, react_1.useState)(null);
51
+ const recorder = (0, expo_audio_1.useAudioRecorder)(expo_audio_1.RecordingPresets.HIGH_QUALITY);
52
+ const startRecording = async () => {
53
+ try {
54
+ const permission = await (0, expo_audio_1.getRecordingPermissionsAsync)();
55
+ if (permission.status !== 'granted') {
56
+ const resp = await (0, expo_audio_1.requestRecordingPermissionsAsync)();
57
+ if (resp.status !== 'granted') {
58
+ react_native_1.Alert.alert('Permission to access microphone was denied');
59
+ return;
60
+ }
61
+ }
62
+ await (0, expo_audio_1.setAudioModeAsync)({
63
+ allowsRecording: true,
64
+ playsInSilentMode: true,
65
+ shouldPlayInBackground: false,
66
+ interruptionMode: 'doNotMix',
67
+ });
68
+ await recorder.prepareToRecordAsync();
69
+ recorder.record();
70
+ }
71
+ catch (err) {
72
+ console.error('Failed to start recording', err);
73
+ }
74
+ };
75
+ const stopRecording = async () => {
76
+ if (!recorder.isRecording)
77
+ return;
78
+ try {
79
+ await recorder.stop();
80
+ const uri = recorder.uri;
81
+ if (uri) {
82
+ setAudioUri(uri);
83
+ setAudioName('recording.m4a');
84
+ }
85
+ }
86
+ catch (err) {
87
+ console.error('Failed to stop recording', err);
88
+ }
89
+ };
90
+ const pickFile = async () => {
91
+ try {
92
+ const result = await DocumentPicker.getDocumentAsync({
93
+ type: ['audio/*'],
94
+ copyToCacheDirectory: true,
95
+ });
96
+ if (!result.canceled && result.assets.length > 0) {
97
+ setAudioUri(result.assets[0].uri);
98
+ setAudioName(result.assets[0].name);
99
+ }
100
+ }
101
+ catch (err) {
102
+ console.error('Failed to pick document', err);
103
+ }
104
+ };
105
+ const clearAudio = () => {
106
+ setAudioUri(null);
107
+ setAudioName(null);
108
+ };
109
+ const handleSubmit = async () => {
110
+ if (!name || !referenceText || !audioUri || !audioName) {
111
+ react_native_1.Alert.alert('Error', 'Please fill out all fields and provide audio.');
112
+ return;
113
+ }
114
+ setIsSubmitting(true);
115
+ try {
116
+ const profile = await (0, api_1.createVoiceProfile)(name);
117
+ await (0, api_1.uploadVoiceSample)(profile.id, audioUri, audioName, referenceText);
118
+ // Reset form
119
+ setName('');
120
+ setReferenceText('');
121
+ clearAudio();
122
+ sheetRef.current?.close();
123
+ onSuccess();
124
+ }
125
+ catch (e) {
126
+ console.error(e);
127
+ react_native_1.Alert.alert('Error', 'Failed to create profile: ' + (e instanceof Error ? e.message : 'Unknown error'));
128
+ }
129
+ finally {
130
+ setIsSubmitting(false);
131
+ }
132
+ };
133
+ const renderBackdrop = (0, react_1.useCallback)((props) => ((0, jsx_runtime_1.jsx)(bottom_sheet_1.BottomSheetBackdrop, { ...props, disappearsOnIndex: -1, appearsOnIndex: 0, opacity: 0.7 })), []);
134
+ return ((0, jsx_runtime_1.jsx)(bottom_sheet_1.default, { ref: sheetRef, index: -1, snapPoints: ['80%'], enablePanDownToClose: true, backdropComponent: renderBackdrop, backgroundStyle: styles.sheetBackground, handleIndicatorStyle: styles.indicator, children: (0, jsx_runtime_1.jsxs)(bottom_sheet_1.BottomSheetScrollView, { style: styles.scrollView, contentContainerStyle: styles.contentContainer, keyboardShouldPersistTaps: "handled", keyboardDismissMode: "interactive", showsVerticalScrollIndicator: false, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.title, children: "Clone Voice" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Profile Name" }), (0, jsx_runtime_1.jsx)(bottom_sheet_1.BottomSheetTextInput, { style: styles.input, placeholder: "e.g. My Custom Voice", placeholderTextColor: "#666", value: name, onChangeText: setName }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Transcript (Reference Text)" }), (0, jsx_runtime_1.jsx)(bottom_sheet_1.BottomSheetTextInput, { style: [styles.input, styles.textArea], placeholder: "Type exactly what is spoken in the audio...", placeholderTextColor: "#666", value: referenceText, onChangeText: setReferenceText, multiline: true }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Audio Source" }), !audioUri ? ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.audioButtonsRow, children: [(0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { style: [styles.audioButton, recorder.isRecording && styles.recordingButton], onPress: recorder.isRecording ? stopRecording : startRecording, children: [(0, jsx_runtime_1.jsx)(vector_icons_1.Ionicons, { name: recorder.isRecording ? 'stop-circle' : 'mic', size: 24, color: recorder.isRecording ? '#fff' : '#6c63ff' }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.audioButtonText, recorder.isRecording && styles.recordingText], children: recorder.isRecording ? 'Stop Recording' : 'Record Voice' })] }), (0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { style: styles.audioButton, onPress: pickFile, children: [(0, jsx_runtime_1.jsx)(vector_icons_1.Ionicons, { name: "folder-open", size: 24, color: "#6c63ff" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.audioButtonText, children: "Upload File" })] })] })) : ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.audioSelected, children: [(0, jsx_runtime_1.jsx)(vector_icons_1.Ionicons, { name: "musical-notes", size: 24, color: "#6c63ff" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.audioNameText, numberOfLines: 1, ellipsizeMode: "middle", children: audioName }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: clearAudio, style: styles.clearButton, children: (0, jsx_runtime_1.jsx)(vector_icons_1.Ionicons, { name: "close-circle", size: 24, color: "#ef4444" }) })] })), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [
135
+ styles.submitButton,
136
+ (!name || !referenceText || !audioUri || isSubmitting) && styles.submitButtonDisabled,
137
+ ], onPress: handleSubmit, disabled: !name || !referenceText || !audioUri || isSubmitting, children: isSubmitting ? ((0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { color: "#fff" })) : ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.submitButtonText, children: "Create Profile" })) })] }) }));
138
+ }
139
+ const styles = react_native_1.StyleSheet.create({
140
+ sheetBackground: {
141
+ backgroundColor: '#161d28',
142
+ },
143
+ indicator: {
144
+ backgroundColor: '#444',
145
+ },
146
+ scrollView: {
147
+ flex: 1,
148
+ },
149
+ contentContainer: {
150
+ padding: 24,
151
+ paddingBottom: 40,
152
+ },
153
+ title: {
154
+ fontSize: 24,
155
+ fontWeight: 'bold',
156
+ color: '#fff',
157
+ marginBottom: 24,
158
+ textAlign: 'center',
159
+ },
160
+ label: {
161
+ color: '#97a3b5',
162
+ fontSize: 12,
163
+ fontWeight: '700',
164
+ textTransform: 'uppercase',
165
+ marginBottom: 8,
166
+ marginTop: 16,
167
+ },
168
+ input: {
169
+ backgroundColor: '#1a1830',
170
+ borderWidth: 1,
171
+ borderColor: '#232933',
172
+ borderRadius: 12,
173
+ color: '#fff',
174
+ padding: 16,
175
+ fontSize: 16,
176
+ },
177
+ textArea: {
178
+ height: 100,
179
+ textAlignVertical: 'top',
180
+ },
181
+ audioButtonsRow: {
182
+ flexDirection: 'row',
183
+ gap: 12,
184
+ },
185
+ audioButton: {
186
+ flex: 1,
187
+ backgroundColor: '#1a1830',
188
+ borderWidth: 1,
189
+ borderColor: '#232933',
190
+ borderRadius: 12,
191
+ padding: 16,
192
+ alignItems: 'center',
193
+ justifyContent: 'center',
194
+ flexDirection: 'row',
195
+ gap: 8,
196
+ },
197
+ recordingButton: {
198
+ backgroundColor: '#ef4444',
199
+ borderColor: '#ef4444',
200
+ },
201
+ audioButtonText: {
202
+ color: '#6c63ff',
203
+ fontWeight: '600',
204
+ fontSize: 14,
205
+ },
206
+ recordingText: {
207
+ color: '#fff',
208
+ },
209
+ audioSelected: {
210
+ flexDirection: 'row',
211
+ alignItems: 'center',
212
+ backgroundColor: '#1a1830',
213
+ borderWidth: 1,
214
+ borderColor: '#6c63ff',
215
+ borderRadius: 12,
216
+ padding: 16,
217
+ },
218
+ audioNameText: {
219
+ color: '#fff',
220
+ marginLeft: 12,
221
+ flex: 1,
222
+ },
223
+ clearButton: {
224
+ marginLeft: 12,
225
+ },
226
+ submitButton: {
227
+ backgroundColor: '#6c63ff',
228
+ borderRadius: 12,
229
+ padding: 16,
230
+ alignItems: 'center',
231
+ marginTop: 24,
232
+ marginBottom: 24,
233
+ },
234
+ submitButtonDisabled: {
235
+ backgroundColor: '#444',
236
+ },
237
+ submitButtonText: {
238
+ color: '#fff',
239
+ fontWeight: 'bold',
240
+ fontSize: 16,
241
+ },
242
+ });
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ interface DetailsPanelProps {
3
+ name: string;
4
+ setName: (v: string) => void;
5
+ description: string;
6
+ setDescription: (v: string) => void;
7
+ isPublic: boolean;
8
+ setIsPublic: (v: boolean) => void;
9
+ isDirty: boolean;
10
+ saving: boolean;
11
+ onSave: () => void;
12
+ onDelete: () => void;
13
+ }
14
+ export declare const DetailsPanel: React.FC<DetailsPanelProps>;
15
+ export default DetailsPanel;