Update static/index.html
Browse files- static/index.html +62 -51
static/index.html
CHANGED
@@ -289,15 +289,12 @@
|
|
289 |
<div class="welcome-screen">
|
290 |
<h2>Welcome to Athspi!</h2>
|
291 |
<p>Hello! I'm your AI assistant Athspi powered by Gemini. Ask me anything - I can explain concepts, generate ideas, or help with coding!</p>
|
292 |
-
<p>
|
293 |
</div>
|
294 |
</main>
|
295 |
|
296 |
<footer class="input-area">
|
297 |
<form id="chat-form" class="input-wrapper">
|
298 |
-
<button type="button" class="icon-button" id="attach-button">
|
299 |
-
<svg viewBox="0 0 24 24"><path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5a2.5 2.5 0 0 1 5 0v10.5c0 .83-.67 1.5-1.5 1.5s-1.5-.67-1.5-1.5V6H10v9.5a2.5 2.5 0 0 0 5 0V5c-1.38 0-2.5 1.12-2.5 2.5v10.5c0 1.38-1.12 2.5-2.5 2.5s-2.5-1.12-2.5-2.5V5a4 4 0 0 1 8 0v11.5c-1.1 0-2-.9-2-2V6h-1.5z"></path></svg>
|
300 |
-
</button>
|
301 |
<textarea id="message-input" placeholder="Ask anything..." rows="1"></textarea>
|
302 |
<button type="submit" id="send-button" class="icon-button send-button disabled">
|
303 |
<svg viewBox="0 0 24 24"><path d="M2 21l21-9L2 3v7l15 2-15 2v7z"></path></svg>
|
@@ -313,7 +310,6 @@
|
|
313 |
const chatArea = document.querySelector('.chat-area');
|
314 |
const welcomeScreen = document.querySelector('.welcome-screen');
|
315 |
const sendButton = document.getElementById('send-button');
|
316 |
-
const attachButton = document.getElementById('attach-button');
|
317 |
|
318 |
let currentAudio = null;
|
319 |
|
@@ -333,7 +329,7 @@
|
|
333 |
chatArea.scrollTop = chatArea.scrollHeight;
|
334 |
};
|
335 |
|
336 |
-
const addMessage = (content, sender, isHTML = false, plainText = '') => {
|
337 |
welcomeScreen.classList.add('hidden');
|
338 |
|
339 |
const messageElement = document.createElement('div');
|
@@ -348,19 +344,30 @@
|
|
348 |
}
|
349 |
messageElement.appendChild(contentDiv);
|
350 |
|
351 |
-
if (sender === 'ai'
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
364 |
}
|
365 |
|
366 |
chatArea.appendChild(messageElement);
|
@@ -377,7 +384,7 @@
|
|
377 |
return indicatorElement;
|
378 |
};
|
379 |
|
380 |
-
const playAudio = async (buttonElement, text) => {
|
381 |
if (currentAudio) {
|
382 |
currentAudio.pause();
|
383 |
currentAudio.currentTime = 0;
|
@@ -388,19 +395,23 @@
|
|
388 |
buttonElement.disabled = true;
|
389 |
|
390 |
try {
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
throw new Error(data.error);
|
|
|
|
|
401 |
}
|
402 |
-
|
403 |
-
|
|
|
|
|
404 |
currentAudio.play();
|
405 |
|
406 |
currentAudio.onended = () => {
|
@@ -410,16 +421,15 @@
|
|
410 |
};
|
411 |
|
412 |
currentAudio.onerror = () => {
|
413 |
-
console.error("Error playing audio
|
414 |
-
alert("Could not play audio. Please try again.");
|
415 |
buttonElement.classList.remove('loading');
|
416 |
buttonElement.disabled = false;
|
417 |
currentAudio = null;
|
418 |
};
|
419 |
|
420 |
} catch (error) {
|
421 |
-
console.error("
|
422 |
-
alert("Failed to
|
423 |
buttonElement.classList.remove('loading');
|
424 |
buttonElement.disabled = false;
|
425 |
}
|
@@ -431,9 +441,7 @@
|
|
431 |
try {
|
432 |
const response = await fetch('/chat', {
|
433 |
method: 'POST',
|
434 |
-
headers: {
|
435 |
-
'Content-Type': 'application/json',
|
436 |
-
},
|
437 |
body: JSON.stringify({ message: userMessage })
|
438 |
});
|
439 |
|
@@ -444,7 +452,21 @@
|
|
444 |
}
|
445 |
|
446 |
chatArea.removeChild(indicator);
|
447 |
-
addMessage(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
448 |
|
449 |
} catch (error) {
|
450 |
chatArea.removeChild(indicator);
|
@@ -475,19 +497,8 @@
|
|
475 |
|
476 |
messageInput.addEventListener('input', adjustTextareaHeight);
|
477 |
|
478 |
-
attachButton.addEventListener('click', () => {
|
479 |
-
alert("Attachment feature not implemented yet!");
|
480 |
-
messageInput.focus();
|
481 |
-
});
|
482 |
-
|
483 |
updateSendButtonState();
|
484 |
messageInput.focus();
|
485 |
-
|
486 |
-
const isFirstVisit = !localStorage.getItem('chatVisited');
|
487 |
-
if (isFirstVisit) {
|
488 |
-
localStorage.setItem('chatVisited', 'true');
|
489 |
-
setTimeout(() => {}, 500);
|
490 |
-
}
|
491 |
});
|
492 |
</script>
|
493 |
</body>
|
|
|
289 |
<div class="welcome-screen">
|
290 |
<h2>Welcome to Athspi!</h2>
|
291 |
<p>Hello! I'm your AI assistant Athspi powered by Gemini. Ask me anything - I can explain concepts, generate ideas, or help with coding!</p>
|
292 |
+
<p>You can request audio responses by saying things like: <em>"Give me the answer in audio"</em> or <em>"Speak the response"</em></p>
|
293 |
</div>
|
294 |
</main>
|
295 |
|
296 |
<footer class="input-area">
|
297 |
<form id="chat-form" class="input-wrapper">
|
|
|
|
|
|
|
298 |
<textarea id="message-input" placeholder="Ask anything..." rows="1"></textarea>
|
299 |
<button type="submit" id="send-button" class="icon-button send-button disabled">
|
300 |
<svg viewBox="0 0 24 24"><path d="M2 21l21-9L2 3v7l15 2-15 2v7z"></path></svg>
|
|
|
310 |
const chatArea = document.querySelector('.chat-area');
|
311 |
const welcomeScreen = document.querySelector('.welcome-screen');
|
312 |
const sendButton = document.getElementById('send-button');
|
|
|
313 |
|
314 |
let currentAudio = null;
|
315 |
|
|
|
329 |
chatArea.scrollTop = chatArea.scrollHeight;
|
330 |
};
|
331 |
|
332 |
+
const addMessage = (content, sender, isHTML = false, plainText = '', audioUrl = null) => {
|
333 |
welcomeScreen.classList.add('hidden');
|
334 |
|
335 |
const messageElement = document.createElement('div');
|
|
|
344 |
}
|
345 |
messageElement.appendChild(contentDiv);
|
346 |
|
347 |
+
if (sender === 'ai') {
|
348 |
+
// Add audio button if audio is available or if response starts with π
|
349 |
+
const shouldShowAudio = audioUrl || content.startsWith("π");
|
350 |
+
const audioText = plainText || content;
|
351 |
+
|
352 |
+
if (shouldShowAudio) {
|
353 |
+
const audioButton = document.createElement('button');
|
354 |
+
audioButton.classList.add('audio-button');
|
355 |
+
audioButton.dataset.audioUrl = audioUrl;
|
356 |
+
audioButton.dataset.plainText = audioText.replace(/^π\s*/, '');
|
357 |
+
audioButton.innerHTML = `
|
358 |
+
<svg class="speaker-icon" viewBox="0 0 24 24">
|
359 |
+
<path d="M3 10v4c0 .55.45 1 1 1h3.5l5.5 5V5L7.5 9H4c-.55 0-1 .45-1 1zm14 0h-1.5c-2.31 0-4.22-1.74-4.47-4H10v12h1.5v-2.22c.25-2.26 2.16-4.22 4.47-4.22H17c1.1 0 2 .9 2 2s-.9 2-2 2h-1.5v2H17c2.21 0 4-1.79 4-4s-1.79-4-4-4z"/>
|
360 |
+
</svg>
|
361 |
+
<div class="loader"></div>
|
362 |
+
`;
|
363 |
+
audioButton.title = "Listen to response";
|
364 |
+
audioButton.addEventListener('click', (e) => {
|
365 |
+
const url = e.currentTarget.dataset.audioUrl;
|
366 |
+
const text = e.currentTarget.dataset.plainText;
|
367 |
+
playAudio(e.currentTarget, url, text);
|
368 |
+
});
|
369 |
+
messageElement.appendChild(audioButton);
|
370 |
+
}
|
371 |
}
|
372 |
|
373 |
chatArea.appendChild(messageElement);
|
|
|
384 |
return indicatorElement;
|
385 |
};
|
386 |
|
387 |
+
const playAudio = async (buttonElement, audioUrl, text) => {
|
388 |
if (currentAudio) {
|
389 |
currentAudio.pause();
|
390 |
currentAudio.currentTime = 0;
|
|
|
395 |
buttonElement.disabled = true;
|
396 |
|
397 |
try {
|
398 |
+
// If we don't have a URL but have text, generate audio first
|
399 |
+
if (!audioUrl && text) {
|
400 |
+
const response = await fetch('/generate-audio', {
|
401 |
+
method: 'POST',
|
402 |
+
headers: { 'Content-Type': 'application/json' },
|
403 |
+
body: JSON.stringify({ text: text })
|
404 |
+
});
|
405 |
+
|
406 |
+
const data = await response.json();
|
407 |
+
if (data.error) throw new Error(data.error);
|
408 |
+
audioUrl = data.audio_url;
|
409 |
+
buttonElement.dataset.audioUrl = audioUrl;
|
410 |
}
|
411 |
+
|
412 |
+
if (!audioUrl) throw new Error("No audio available");
|
413 |
+
|
414 |
+
currentAudio = new Audio(audioUrl);
|
415 |
currentAudio.play();
|
416 |
|
417 |
currentAudio.onended = () => {
|
|
|
421 |
};
|
422 |
|
423 |
currentAudio.onerror = () => {
|
424 |
+
console.error("Error playing audio");
|
|
|
425 |
buttonElement.classList.remove('loading');
|
426 |
buttonElement.disabled = false;
|
427 |
currentAudio = null;
|
428 |
};
|
429 |
|
430 |
} catch (error) {
|
431 |
+
console.error("Audio error:", error);
|
432 |
+
alert("Failed to play audio. " + (error.message || "Please try again."));
|
433 |
buttonElement.classList.remove('loading');
|
434 |
buttonElement.disabled = false;
|
435 |
}
|
|
|
441 |
try {
|
442 |
const response = await fetch('/chat', {
|
443 |
method: 'POST',
|
444 |
+
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
445 |
body: JSON.stringify({ message: userMessage })
|
446 |
});
|
447 |
|
|
|
452 |
}
|
453 |
|
454 |
chatArea.removeChild(indicator);
|
455 |
+
addMessage(
|
456 |
+
data.response_html,
|
457 |
+
'ai',
|
458 |
+
true,
|
459 |
+
data.response_text,
|
460 |
+
data.audio_url
|
461 |
+
);
|
462 |
+
|
463 |
+
// Auto-play audio if response starts with π
|
464 |
+
if (data.response_text.startsWith("π")) {
|
465 |
+
const audioBtn = document.querySelector('.ai-message:last-child .audio-button');
|
466 |
+
if (audioBtn) {
|
467 |
+
setTimeout(() => audioBtn.click(), 500);
|
468 |
+
}
|
469 |
+
}
|
470 |
|
471 |
} catch (error) {
|
472 |
chatArea.removeChild(indicator);
|
|
|
497 |
|
498 |
messageInput.addEventListener('input', adjustTextareaHeight);
|
499 |
|
|
|
|
|
|
|
|
|
|
|
500 |
updateSendButtonState();
|
501 |
messageInput.focus();
|
|
|
|
|
|
|
|
|
|
|
|
|
502 |
});
|
503 |
</script>
|
504 |
</body>
|