diff --git a/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx b/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx index 554e724..ff6a3a3 100644 --- a/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx +++ b/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx @@ -17,7 +17,8 @@ import { emotionOptions, geminiVoices, openaiVoices, r2UrlAudio } from "@/lib/da import EmojiComponent from "./EmojiComponent"; import { PitchFactors } from "@/lib/utils"; import { Slider } from "@/components/ui/slider"; -import ElevenLabsModal from "./ElevenLabsModal"; +import VoiceCloneModal from "./VoiceCloneModal"; +import Image from "next/image"; interface SettingsDashboardProps { selectedUser: IUser; @@ -63,9 +64,7 @@ const SettingsDashboard: React.FC = ({ }); const [touchedFields, setTouchedFields] = useState>({}); - const [formErrors, setFormErrors] = useState>>({}); - const [previewingVoice, setPreviewingVoice] = useState(null); const handleBlur = (field: keyof FormData | 'features') => { @@ -204,7 +203,13 @@ const SettingsDashboard: React.FC = ({ }; const [audioElement, setAudioElement] = useState(null); - const [showElevenLabsModal, setShowElevenLabsModal] = useState(false); + const [showVoiceCloneModal, setShowVoiceCloneModal] = useState<{ + provider: "elevenlabs" | "hume"; + title: string; + voiceInputLabel: string; + voiceInputPlaceholder: string; + voiceDescription: string; +} | null>(null); const previewVoice = (voice: VoiceType) => { @@ -326,24 +331,37 @@ const SettingsDashboard: React.FC = ({ {/* ElevenLabs Alternative */}
-
+

- Create a voice clone with Eleven Labs Conversational AI Agents or Hume EVI4 + Create Voice AI Characters with 11Labs Conversational AI Agents or Hume EVI4

- + +
+
@@ -544,17 +562,19 @@ const SettingsDashboard: React.FC = ({
)} - setShowElevenLabsModal(false)} + setShowVoiceCloneModal(null)} selectedUser={selectedUser} onSuccess={() => { // Optionally refresh personalities or show success message toast({ title: "Success", - description: "ElevenLabs character added successfully!", + description: "Voice clone character added successfully!", }); + router.push('/home'); }} + voiceCloneModalProps={showVoiceCloneModal!} /> ) diff --git a/frontend-nextjs/app/components/CreateCharacter/ElevenLabsModal.tsx b/frontend-nextjs/app/components/CreateCharacter/VoiceCloneModal.tsx similarity index 75% rename from frontend-nextjs/app/components/CreateCharacter/ElevenLabsModal.tsx rename to frontend-nextjs/app/components/CreateCharacter/VoiceCloneModal.tsx index d91eec0..bd45f2e 100644 --- a/frontend-nextjs/app/components/CreateCharacter/ElevenLabsModal.tsx +++ b/frontend-nextjs/app/components/CreateCharacter/VoiceCloneModal.tsx @@ -12,15 +12,24 @@ import { createClient } from "@/utils/supabase/client"; import { createPersonality } from "@/db/personalities"; import { v4 as uuidv4 } from 'uuid'; -interface ElevenLabsModalProps { +interface VoiceCloneModalProps { isOpen: boolean; onClose: () => void; onSuccess?: () => void; selectedUser: IUser; + voiceCloneModalProps: { + provider: "elevenlabs" | "hume"; + title: string; + voiceInputLabel: string; + voiceInputPlaceholder: string; + voiceDescription: string; + }; } -export default function ElevenLabsModal({ isOpen, onClose, onSuccess, selectedUser }: ElevenLabsModalProps) { - const [isSubmitting, setIsSubmitting] = useState(false); +export default function VoiceCloneModal({ isOpen, onClose, onSuccess, selectedUser, voiceCloneModalProps }: VoiceCloneModalProps) { +if (!voiceCloneModalProps) return null; + const { provider, title, voiceInputLabel, voiceInputPlaceholder, voiceDescription } = voiceCloneModalProps; +const [isSubmitting, setIsSubmitting] = useState(false); const [form, setForm] = useState({ name: '', agentId: '' @@ -35,7 +44,7 @@ export default function ElevenLabsModal({ isOpen, onClose, onSuccess, selectedUs try { const personality = await createPersonality(supabase, selectedUser.user_id, { - provider: 'elevenlabs' as ModelProvider, + provider: provider, title: form.name, subtitle: "", character_prompt: "", @@ -54,11 +63,11 @@ export default function ElevenLabsModal({ isOpen, onClose, onSuccess, selectedUs if (personality) { onClose(); setForm({ name: '', agentId: '' }); - toast({ description: 'ElevenLabs character added successfully!' }); + toast({ description: `${provider} character added successfully!` }); onSuccess?.(); } } catch (error) { - console.error('Error creating ElevenLabs character:', error); + console.error(`Error creating ${provider} character:`, error); toast({ description: 'Failed to create character. Please try again.', variant: "destructive" }); } finally { setIsSubmitting(false); @@ -84,7 +93,7 @@ export default function ElevenLabsModal({ isOpen, onClose, onSuccess, selectedUs
- +

- Find this in your Eleven Labs dashboard under your agent settings + {voiceDescription}

@@ -125,12 +134,18 @@ export default function ElevenLabsModal({ isOpen, onClose, onSuccess, selectedUs ), [form.name, form.agentId, handleSubmit, handleNameChange, handleAgentIdChange, handleClose, isSubmitting]); + const handleOpenChange = useCallback((open: boolean) => { + if (!open) { + handleClose(); + } + }, [handleClose]); + if (isDesktop) { return ( - + - Add Eleven Labs Character + {title} {FormContent} @@ -139,10 +154,10 @@ export default function ElevenLabsModal({ isOpen, onClose, onSuccess, selectedUs } return ( - + - Add Eleven Labs Character + {title}
{FormContent} diff --git a/frontend-nextjs/app/components/Nav/NavbarButtons.tsx b/frontend-nextjs/app/components/Nav/NavbarButtons.tsx index 54ade19..4f334a5 100644 --- a/frontend-nextjs/app/components/Nav/NavbarButtons.tsx +++ b/frontend-nextjs/app/components/Nav/NavbarButtons.tsx @@ -31,7 +31,7 @@ const NavbarButtons: React.FC = ({ } items-center font-bold text-sm `} > {isHome && user && ( -
+
)} diff --git a/frontend-nextjs/public/logos/elevenlabs.svg b/frontend-nextjs/public/logos/elevenlabs.svg new file mode 100644 index 0000000..cf6b615 --- /dev/null +++ b/frontend-nextjs/public/logos/elevenlabs.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend-nextjs/public/logos/hume.png b/frontend-nextjs/public/logos/hume.png new file mode 100644 index 0000000..9d37501 Binary files /dev/null and b/frontend-nextjs/public/logos/hume.png differ diff --git a/frontend-nextjs/public/logos/humeai.avif b/frontend-nextjs/public/logos/humeai.avif new file mode 100644 index 0000000..f046726 Binary files /dev/null and b/frontend-nextjs/public/logos/humeai.avif differ diff --git a/server-deno/models/hume.ts b/server-deno/models/hume.ts index 8c65d69..60beff8 100644 --- a/server-deno/models/hume.ts +++ b/server-deno/models/hume.ts @@ -27,7 +27,7 @@ export const connectToHume = ({ // Build Hume WebSocket URL const queryParams = new URLSearchParams({ api_key: humeApiKey!, - config_id: personality!.voice?.config.config_id, + config_id: personality!.oai_voice, }); const humeWsUrl = `wss://api.hume.ai/v0/evi/chat?${queryParams.toString()}`;