Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import Config from './lib/config';
import Storage from './lib/storage';
import Voice from './lib/voice';
import useVoices from './hooks/useVoices';
import textToSpeech from "./lib/eleven_labs";

interface CreateChatGPTMessageResponse {
answer: string;
Expand Down Expand Up @@ -238,7 +239,7 @@ function App() {
...oldMessages,
{ type: 'response', text: res.answer },
]);
speak(res.answer);
textToSpeech(res.answer);
})
.catch((err: unknown) => {
console.warn(err);
Expand Down
9 changes: 8 additions & 1 deletion src/design_system/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { KeyboardEventHandler } from 'react';
import { DollarSign, Terminal } from 'react-feather';
import Typewriter from './typewriter';

interface MessageProps {
type: 'prompt' | 'response';
Expand Down Expand Up @@ -47,7 +48,13 @@ export default function Message({
</span>
)}
</div>
<div className="font-medium text-2xl">{text}</div>
<div className="font-medium text-2xl">
{type === 'response' ? (
<Typewriter text={text} typingDelay={250} />
) : (
<span>{text}</span>
)}
</div>
</div>
);
}
30 changes: 30 additions & 0 deletions src/design_system/typewriter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useEffect, useState } from 'react';
import styles from './typing.module.css';

const Typewriter = ({ text = "", typingDelay = 100, blinkingCursor = true }) => {
const [typewriterText, setTypewriterText] = useState("");
const [index, setIndex] = useState(0);
const words = text.replace(/\n/g, '<br/>').split(' ');

useEffect(() => {
setTypewriterText("");
setIndex(0);
}, [text]);

useEffect(() => {
if (index < words.length) {
const timerId = setInterval(() => {
setTypewriterText((prevText) => prevText + (index > 0 ? ' ' : '') + words[index]);
setIndex((prevIndex) => prevIndex + 1);
}, typingDelay);

return () => clearInterval(timerId);
}
}, [index, words, typingDelay]);

return (
<span className={blinkingCursor ? styles.blinkingCursor : ''} dangerouslySetInnerHTML={{ __html: typewriterText }}></span>
);
};

export default Typewriter;
7 changes: 7 additions & 0 deletions src/design_system/typing.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.blinking-cursor {
animation: blink 0.75s step-end infinite;
}

@keyframes blink {
50% { opacity: 0 }
}
36 changes: 36 additions & 0 deletions src/lib/eleven_labs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Define your Eleven Labs API key
const ELEVEN_LABS_API_KEY = ""; // Add your Eleven Labs API key here
const sVoiceId = "21m00Tcm4TlvDq8ikWAM"; // This is the default voiceId. You can change it if needed.

// Function for making a request to Eleven Labs API and playing the response
export default async function textToSpeech(text: string, voiceId: string = sVoiceId): Promise<void> {
// Preparing the request
const url = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`;
const headers = new Headers();
headers.append("Accept", "audio/mpeg");
headers.append("Content-Type", "application/json");
headers.append("xi-api-key", ELEVEN_LABS_API_KEY);
const data = {
text: text,
voice_settings: { stability: 0, similarity_boost: 0 }
};
const requestOptions: RequestInit = {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
};

// Making the request
const response = await fetch(url, requestOptions);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

// Reading the response as Blob
const blob = await response.blob();

// Creating object URL and audio element to play the sound
const audioURL = URL.createObjectURL(blob);
const audio = new Audio(audioURL);
audio.play();
}