{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "conversation-bar", "dependencies": [ "@elevenlabs/react" ], "registryDependencies": [ "button", "https://ui.elevenlabs.io/r/live-waveform.json", "card", "separator", "textarea" ], "files": [ { "path": "components/ui/conversation-bar.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n useConversationControls,\n useConversationInput,\n useConversationStatus,\n} from \"@elevenlabs/react\"\nimport {\n ArrowUpIcon,\n ChevronDown,\n Keyboard,\n Mic,\n MicOff,\n PhoneIcon,\n XIcon,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card } from \"@/components/ui/card\"\nimport { LiveWaveform } from \"@/components/ui/live-waveform\"\nimport { Separator } from \"@/components/ui/separator\"\nimport { Textarea } from \"@/components/ui/textarea\"\n\nexport interface ConversationBarProps {\n /**\n * ElevenLabs Agent ID to connect to\n */\n agentId: string\n\n /**\n * Custom className for the container\n */\n className?: string\n\n /**\n * Custom className for the waveform\n */\n waveformClassName?: string\n\n /**\n * Callback when user sends a message\n */\n onSendMessage?: (message: string) => void\n}\n\nexport const ConversationBar = React.forwardRef<\n HTMLDivElement,\n ConversationBarProps\n>(({ agentId, className, waveformClassName, onSendMessage }, ref) => {\n const { status } = useConversationStatus()\n const { startSession, endSession, sendUserMessage, sendContextualUpdate } =\n useConversationControls()\n const { isMuted, setMuted } = useConversationInput()\n const [keyboardOpen, setKeyboardOpen] = React.useState(false)\n const [textInput, setTextInput] = React.useState(\"\")\n const mediaStreamRef = React.useRef(null)\n\n const isConnected = status === \"connected\"\n\n const getMicStream = React.useCallback(async () => {\n if (mediaStreamRef.current) return mediaStreamRef.current\n\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\n mediaStreamRef.current = stream\n\n return stream\n }, [])\n\n const startConversation = React.useCallback(async () => {\n try {\n await getMicStream()\n\n startSession({\n agentId,\n connectionType: \"webrtc\",\n })\n } catch (error) {\n console.error(\"Error starting conversation:\", error)\n }\n }, [getMicStream, agentId, startSession])\n\n const handleEndSession = React.useCallback(() => {\n endSession()\n\n if (mediaStreamRef.current) {\n mediaStreamRef.current.getTracks().forEach((t) => t.stop())\n mediaStreamRef.current = null\n }\n }, [endSession])\n\n const toggleMute = React.useCallback(() => {\n setMuted(!isMuted)\n }, [isMuted, setMuted])\n\n const handleStartOrEnd = React.useCallback(() => {\n if (status === \"connected\" || status === \"connecting\") {\n handleEndSession()\n } else if (status === \"disconnected\" || status === \"error\") {\n startConversation()\n }\n }, [status, handleEndSession, startConversation])\n\n const handleSendText = React.useCallback(() => {\n if (!textInput.trim()) return\n\n const messageToSend = textInput\n sendUserMessage(messageToSend)\n setTextInput(\"\")\n onSendMessage?.(messageToSend)\n }, [sendUserMessage, textInput, onSendMessage])\n\n const handleTextChange = React.useCallback(\n (e: React.ChangeEvent) => {\n const value = e.target.value\n setTextInput(value)\n\n if (value.trim() && isConnected) {\n sendContextualUpdate(value)\n }\n },\n [sendContextualUpdate, isConnected]\n )\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault()\n handleSendText()\n }\n },\n [handleSendText]\n )\n\n React.useEffect(() => {\n return () => {\n if (mediaStreamRef.current) {\n mediaStreamRef.current.getTracks().forEach((t) => t.stop())\n }\n }\n }, [])\n\n return (\n \n \n
\n
\n {keyboardOpen && }\n
\n
\n \n
\n \n \n {(status === \"disconnected\" || status === \"error\") && (\n
\n \n Customer Support\n \n
\n )}\n
\n
\n
\n
\n
\n \n {isMuted ? : }\n \n setKeyboardOpen((v) => !v)}\n aria-pressed={keyboardOpen}\n className=\"relative\"\n disabled={!isConnected}\n >\n \n \n \n \n \n
\n
\n \n\n \n
\n \n \n \n \n
\n \n \n
\n \n )\n})\n\nConversationBar.displayName = \"ConversationBar\"\n", "type": "registry:ui" } ], "type": "registry:ui" }