Skip to content

Commit

Permalink
Fix export/import issues in chat components
Browse files Browse the repository at this point in the history
  • Loading branch information
while-basic committed Dec 2, 2024
1 parent 6bf4375 commit 5fd9925
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 146 deletions.
2 changes: 1 addition & 1 deletion components/chat/chat-interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState } from 'react'
import { MessageList } from './message-list'
import { ImageGeneration } from './image-generation'
import RealtimeChat from './realtime-chat'
import { RealtimeChat } from './realtime-chat'
import { Message, Conversation, TokenUsage } from '@/lib/chat'
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
Expand Down
149 changes: 23 additions & 126 deletions components/chat/realtime-chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import { useState, useEffect, useRef } from 'react'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Mic, MicOff, StopCircle } from 'lucide-react'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Mic, StopCircle } from 'lucide-react'

interface Message {
role: string;
Expand All @@ -13,29 +11,12 @@ interface Message {
cost?: number;
}

const AVAILABLE_VOICES = [
{ id: 'alloy', name: 'Alloy' },
{ id: 'echo', name: 'Echo' },
{ id: 'shimmer', name: 'Shimmer' },
{ id: 'ash', name: 'Ash' },
{ id: 'ballad', name: 'Ballad' },
{ id: 'coral', name: 'Coral' },
{ id: 'sage', name: 'Sage' },
{ id: 'verse', name: 'Verse' },
] as const

export function RealtimeChat() {
const [messages, setMessages] = useState<Message[]>([])
const [isRecording, setIsRecording] = useState(false)
const [isConnected, setIsConnected] = useState(false)
const [hasAudioPermission, setHasAudioPermission] = useState(false)
const [error, setError] = useState<string | null>(null)
const [messages, setMessages] = useState<Message[]>([])
const [totalCost, setTotalCost] = useState(0)
const [selectedVoice, setSelectedVoice] = useState<string>('alloy')
const [isProcessing, setIsProcessing] = useState(false);
const [audioQueue, setAudioQueue] = useState<Blob[]>([]);
const audioRef = useRef<HTMLAudioElement | null>(null);
const mediaRecorderRef = useRef<MediaRecorder | null>(null)
const chunksRef = useRef<Blob[]>([])

useEffect(() => {
return () => {
Expand All @@ -45,38 +26,9 @@ export function RealtimeChat() {
}
}, [])

const connectMicrophone = async () => {
try {
setError(null);

// First request microphone permission
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
setHasAudioPermission(true);

// Keep track of the stream for later use
const audioTrack = stream.getAudioTracks()[0];
audioTrack.onended = () => {
setHasAudioPermission(false);
setIsConnected(false);
setError('Microphone disconnected');
if (mediaRecorderRef.current) {
mediaRecorderRef.current.stop();
}
};

// Log success
console.log('Microphone connected successfully');

} catch (error) {
console.error('Error accessing microphone:', error);
setError('Could not access microphone. Please ensure you have granted microphone permissions.');
setHasAudioPermission(false);
}
};

const processAudioChunk = async (audioData: Blob) => {
try {
setIsProcessing(true);
setError(null);

// Create form data for the transcription request
const formData = new FormData();
Expand All @@ -103,7 +55,6 @@ export function RealtimeChat() {

if (!transcription.trim()) {
console.log('No speech detected in audio');
setIsProcessing(false);
return;
}

Expand Down Expand Up @@ -142,7 +93,7 @@ export function RealtimeChat() {
},
body: JSON.stringify({
model: 'tts-1',
voice: selectedVoice,
voice: 'alloy',
input: responseText
})
});
Expand All @@ -166,13 +117,9 @@ export function RealtimeChat() {
cost
}]);

setTotalCost(prev => prev + cost);
setIsProcessing(false);

} catch (error) {
console.error('Error processing audio:', error);
setError('Failed to process audio. Please try again.');
setIsProcessing(false);
}
};

Expand All @@ -192,17 +139,17 @@ export function RealtimeChat() {
});
setIsRecording(true);

const chunks: Blob[] = [];
chunksRef.current = [];

// Start collecting audio data
mediaRecorderRef.current.ondataavailable = (event) => {
if (event.data.size > 0) {
chunks.push(event.data);
chunksRef.current.push(event.data);
}
};

mediaRecorderRef.current.onstop = async () => {
const audioBlob = new Blob(chunks, { type: 'audio/webm;codecs=opus' });
const audioBlob = new Blob(chunksRef.current, { type: 'audio/webm;codecs=opus' });
await processAudioChunk(audioBlob);
};

Expand All @@ -225,15 +172,6 @@ export function RealtimeChat() {
}
};

const disconnect = () => {
if (mediaRecorderRef.current) {
mediaRecorderRef.current.stop();
}
setIsRecording(false);
setIsConnected(false);
setError(null);
};

return (
<div className="flex flex-col h-full">
{error && (
Expand Down Expand Up @@ -296,71 +234,30 @@ export function RealtimeChat() {
<div className="flex flex-col space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-2">
<Select
value={selectedVoice}
onValueChange={setSelectedVoice}
<Button
onClick={startRecording}
disabled={isRecording}
className="bg-green-500 hover:bg-green-600"
>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select voice" />
</SelectTrigger>
<SelectContent>
{AVAILABLE_VOICES.map((voice) => (
<SelectItem key={voice.id} value={voice.id}>
{voice.name}
</SelectItem>
))}
</SelectContent>
</Select>

{isProcessing && (
<span className="text-sm text-muted-foreground animate-pulse">
Processing audio...
</span>
)}
<Mic className="h-4 w-4 mr-2" />
Start Recording
</Button>
</div>

<div className="text-sm text-muted-foreground">
Total Cost: ${totalCost.toFixed(4)}
Total Cost: $0.0000
</div>
</div>

<div className="flex justify-center space-x-4">
{!hasAudioPermission ? (
<Button onClick={connectMicrophone} disabled={hasAudioPermission}>
<Mic className="h-4 w-4 mr-2" />
Connect Microphone
</Button>
) : !isRecording ? (
<Button
onClick={startRecording}
disabled={isRecording || isProcessing}
className="bg-green-500 hover:bg-green-600"
>
<Mic className="h-4 w-4 mr-2" />
Start Recording
</Button>
) : (
<Button
onClick={stopRecording}
disabled={!isRecording || isProcessing}
className="bg-red-500 hover:bg-red-600"
>
<StopCircle className="h-4 w-4 mr-2" />
Stop Recording
</Button>
)}

{hasAudioPermission && (
<Button
onClick={disconnect}
variant="outline"
disabled={isProcessing}
>
<MicOff className="h-4 w-4 mr-2" />
Disconnect
</Button>
)}
<Button
onClick={stopRecording}
disabled={!isRecording}
className="bg-red-500 hover:bg-red-600"
>
<StopCircle className="h-4 w-4 mr-2" />
Stop Recording
</Button>
</div>

{error && (
Expand Down
38 changes: 19 additions & 19 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://chriscelaya.com/audio-recorder</loc><lastmod>2024-12-02T03:23:40.391Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/auth/sign-in</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/blog/hello-world</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/chat/image</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/blog</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/case-studies</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.9</priority></url>
<url><loc>https://chriscelaya.com/auth/sign-up</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/dashboard</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/audio</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/chat</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/experience</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>
<url><loc>https://chriscelaya.com/links</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/gallery</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>1</priority></url>
<url><loc>https://chriscelaya.com/about</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>
<url><loc>https://chriscelaya.com/projects</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.9</priority></url>
<url><loc>https://chriscelaya.com/resume</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/skills</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/profile</loc><lastmod>2024-12-02T03:23:40.392Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/about</loc><lastmod>2024-12-02T04:26:40.145Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>
<url><loc>https://chriscelaya.com/case-studies</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.9</priority></url>
<url><loc>https://chriscelaya.com/blog/hello-world</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/chat/image</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/experience</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>
<url><loc>https://chriscelaya.com/links</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>1</priority></url>
<url><loc>https://chriscelaya.com/blog</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/auth/sign-up</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/profile</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/chat</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/audio-recorder</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/resume</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/skills</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/gallery</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/auth/sign-in</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/projects</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.9</priority></url>
<url><loc>https://chriscelaya.com/audio</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://chriscelaya.com/dashboard</loc><lastmod>2024-12-02T04:26:40.146Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
</urlset>

0 comments on commit 5fd9925

Please sign in to comment.