talk-to-claude-gradio / index.html
freddyaboulton's picture
Upload folder using huggingface_hub
19b72df verified
raw
history blame
10.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RetroChat Audio</title>
<style>
body {
font-family: monospace;
background-color: #1a1a1a;
color: #00ff00;
margin: 0;
padding: 20px;
height: 100vh;
box-sizing: border-box;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
height: calc(100% - 100px);
margin-bottom: 20px;
}
.visualization-container {
border: 2px solid #00ff00;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
#visualizer {
width: 100%;
height: 100%;
background-color: #000;
}
.chat-container {
border: 2px solid #00ff00;
padding: 20px;
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
}
.chat-messages {
flex-grow: 1;
overflow-y: auto;
margin-bottom: 20px;
padding: 10px;
border: 1px solid #00ff00;
}
.message {
margin-bottom: 10px;
padding: 8px;
border-radius: 4px;
}
.message.user {
background-color: #003300;
}
.message.assistant {
background-color: #002200;
}
.controls {
text-align: center;
}
button {
background-color: #000;
color: #00ff00;
border: 2px solid #00ff00;
padding: 10px 20px;
font-family: monospace;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background-color: #00ff00;
color: #000;
}
#audio-output {
display: none;
}
/* Retro CRT effect */
.crt-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: repeating-linear-gradient(0deg,
rgba(0, 255, 0, 0.03),
rgba(0, 255, 0, 0.03) 1px,
transparent 1px,
transparent 2px);
pointer-events: none;
}
</style>
</head>
<body>
<div class="container">
<div class="visualization-container">
<canvas id="visualizer"></canvas>
<div class="crt-overlay"></div>
</div>
<div class="chat-container">
<div class="chat-messages" id="chat-messages"></div>
</div>
</div>
<div class="controls">
<button id="start-button">Start</button>
</div>
<audio id="audio-output"></audio>
<script>
let audioContext;
let analyser;
let dataArray;
let animationId;
let chatHistory = [];
let peerConnection;
let webrtc_id;
const visualizer = document.getElementById('visualizer');
const ctx = visualizer.getContext('2d');
const audioOutput = document.getElementById('audio-output');
const startButton = document.getElementById('start-button');
const chatMessages = document.getElementById('chat-messages');
// Set canvas size
function resizeCanvas() {
visualizer.width = visualizer.offsetWidth;
visualizer.height = visualizer.offsetHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Initialize WebRTC
async function setupWebRTC() {
const config = __RTC_CONFIGURATION__;
peerConnection = new RTCPeerConnection(config);
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true
});
stream.getTracks().forEach(track => {
peerConnection.addTrack(track, stream);
});
// Audio visualization will be set up when we receive the output stream
// Handle incoming audio
peerConnection.addEventListener('track', (evt) => {
if (audioOutput && audioOutput.srcObject !== evt.streams[0]) {
audioOutput.srcObject = evt.streams[0];
audioOutput.play();
// Set up audio visualization on the output stream
audioContext = new AudioContext();
analyser = audioContext.createAnalyser();
const source = audioContext.createMediaStreamSource(evt.streams[0]);
source.connect(analyser);
analyser.fftSize = 2048;
dataArray = new Uint8Array(analyser.frequencyBinCount);
}
});
// Create data channel for messages
const dataChannel = peerConnection.createDataChannel('text');
dataChannel.onmessage = handleMessage;
// Create and send offer
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
await new Promise((resolve) => {
if (peerConnection.iceGatheringState === "complete") {
resolve();
} else {
const checkState = () => {
if (peerConnection.iceGatheringState === "complete") {
peerConnection.removeEventListener("icegatheringstatechange", checkState);
resolve();
}
};
peerConnection.addEventListener("icegatheringstatechange", checkState);
}
});
webrtc_id = Math.random().toString(36).substring(7);
const response = await fetch('/webrtc/offer', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sdp: peerConnection.localDescription.sdp,
type: peerConnection.localDescription.type,
webrtc_id: webrtc_id
})
});
const serverResponse = await response.json();
await peerConnection.setRemoteDescription(serverResponse);
// Start visualization
draw();
// create event stream to receive messages from /output
const eventSource = new EventSource('/outputs?webrtc_id=' + webrtc_id);
eventSource.addEventListener("output", (event) => {
const eventJson = JSON.parse(event.data);
addMessage(eventJson.role, eventJson.content);
});
} catch (err) {
console.error('Error setting up WebRTC:', err);
}
}
function handleMessage(event) {
const eventJson = JSON.parse(event.data);
if (eventJson.type === "send_input") {
fetch('/input_hook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
webrtc_id: webrtc_id,
chatbot: chatHistory
})
});
}
}
function addMessage(role, content) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message', role);
messageDiv.textContent = content;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
chatHistory.push({ role, content });
}
function draw() {
animationId = requestAnimationFrame(draw);
analyser.getByteTimeDomainData(dataArray);
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0, 0, visualizer.width, visualizer.height);
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgb(0, 255, 0)';
ctx.beginPath();
const sliceWidth = visualizer.width / dataArray.length;
let x = 0;
for (let i = 0; i < dataArray.length; i++) {
const v = dataArray[i] / 128.0;
const y = v * visualizer.height / 2;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
x += sliceWidth;
}
ctx.lineTo(visualizer.width, visualizer.height / 2);
ctx.stroke();
}
function stop() {
if (peerConnection) {
if (peerConnection.getTransceivers) {
peerConnection.getTransceivers().forEach(transceiver => {
if (transceiver.stop) {
transceiver.stop();
}
});
}
if (peerConnection.getSenders) {
peerConnection.getSenders().forEach(sender => {
if (sender.track && sender.track.stop) sender.track.stop();
});
}
setTimeout(() => {
peerConnection.close();
}, 500);
}
if (animationId) {
cancelAnimationFrame(animationId);
}
if (audioContext) {
audioContext.close();
}
}
startButton.addEventListener('click', () => {
if (startButton.textContent === 'Start') {
setupWebRTC();
startButton.textContent = 'Stop';
} else {
stop();
startButton.textContent = 'Start';
}
});
</script>
</body>
</html>