| const API_URL = "/ask"; |
|
|
| |
| const sessionId = "session_" + Math.random().toString(36).substr(2, 9); |
| console.log("Your Session ID:", sessionId); |
|
|
| |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; |
| const recognition = new SpeechRecognition(); |
| recognition.lang = 'en-US'; |
| recognition.interimResults = false; |
|
|
| let isVoiceActive = false; |
|
|
| function startVoice() { |
| const micBtn = document.getElementById("mic-btn"); |
| if (isVoiceActive) { |
| recognition.stop(); |
| return; |
| } |
| try { |
| recognition.start(); |
| micBtn.classList.add("listening"); |
| isVoiceActive = true; |
| } catch (error) { |
| console.error("Speech recognition error:", error); |
| isVoiceActive = false; |
| } |
| } |
|
|
| recognition.onend = () => { |
| document.getElementById("mic-btn").classList.remove("listening"); |
| }; |
|
|
| recognition.onresult = (event) => { |
| const transcript = event.results[0][0].transcript; |
| document.getElementById("user-input").value = transcript; |
| sendMessage(); |
| }; |
|
|
| |
| function speakText(text) { |
| window.speechSynthesis.cancel(); |
| const cleanText = text.replace(/\*/g, "").replace(/<[^>]*>/g, ""); |
| const utterance = new SpeechSynthesisUtterance(cleanText); |
| window.speechSynthesis.speak(utterance); |
| } |
|
|
| function appendMessage(text, sender) { |
| const box = document.getElementById("chat-box"); |
| const msg = document.createElement("div"); |
| msg.classList.add("message", sender); |
|
|
| |
| if (sender === "bot") { |
| |
| let htmlContent = marked.parse(text); |
| msg.innerHTML = htmlContent; |
| } else { |
| |
| msg.textContent = text; |
| } |
|
|
| box.appendChild(msg); |
| box.scrollTop = box.scrollHeight; |
|
|
| |
| if (sender === "bot" && window.MathJax) { |
| MathJax.typesetPromise([msg]).catch((err) => console.log(err)); |
| } |
| } |
| let currentQuizData = []; |
| let currentQuestionIndex = 0; |
| let score = 0; |
|
|
| function renderQuiz(jsonString) { |
| const box = document.getElementById("chat-box"); |
| |
| try { |
| currentQuizData = JSON.parse(jsonString); |
| } catch (e) { |
| appendMessage("β οΈ Error loading quiz.", "bot"); |
| return; |
| } |
|
|
| currentQuestionIndex = 0; |
| score = 0; |
| showNextQuestion(); |
| } |
|
|
| function showNextQuestion() { |
| const box = document.getElementById("chat-box"); |
| |
| |
| if (currentQuestionIndex >= currentQuizData.length) { |
| const resultDiv = document.createElement("div"); |
| resultDiv.className = "message bot"; |
| resultDiv.innerHTML = ` |
| <div class="quiz-container"> |
| <div class="quiz-score"> |
| π Quiz Complete!<br> |
| You scored ${score} / ${currentQuizData.length} |
| </div> |
| </div> |
| `; |
| box.appendChild(resultDiv); |
| box.scrollTop = box.scrollHeight; |
| return; |
| } |
|
|
| const qData = currentQuizData[currentQuestionIndex]; |
| |
| |
| const div = document.createElement("div"); |
| div.className = "message bot"; |
| div.innerHTML = ` |
| <div class="quiz-container" id="question-${currentQuestionIndex}"> |
| <div class="quiz-question">Q${currentQuestionIndex + 1}: ${qData.question}</div> |
| <div class="quiz-options"> |
| ${qData.options.map(opt => |
| `<button class="quiz-btn" onclick="checkAnswer(this, '${opt.replace(/'/g, "\\'")}')">${opt}</button>` |
| ).join('')} |
| </div> |
| </div> |
| `; |
| |
| box.appendChild(div); |
| box.scrollTop = box.scrollHeight; |
| } |
| |
| function checkAnswer(btn, selectedOption) { |
| const qData = currentQuizData[currentQuestionIndex]; |
| const correctOption = qData.correct_answer; |
| const parent = btn.parentElement; |
| |
| // Disable all buttons to prevent double clicking |
| const allBtns = parent.querySelectorAll('.quiz-btn'); |
| allBtns.forEach(b => b.disabled = true); |
| |
| if (selectedOption === correctOption) { |
| btn.classList.add("correct"); |
| score++; |
| } else { |
| btn.classList.add("wrong"); |
| // Highlight the correct one |
| allBtns.forEach(b => { |
| if (b.innerText === correctOption) b.classList.add("correct"); |
| }); |
| } |
| |
| // Wait 1.5 seconds then show next question |
| currentQuestionIndex++; |
| setTimeout(showNextQuestion, 1500); |
| } |
| function appendFlashcards(jsonString) { |
| const box = document.getElementById("chat-box"); |
| |
| // Parse the JSON string from Python |
| let cardsData; |
| try { |
| cardsData = JSON.parse(jsonString); |
| } catch (e) { |
| console.error("JSON Error:", e); |
| appendMessage("β οΈ Error generating cards.", "bot"); |
| return; |
| } |
| |
| // Create Container |
| const container = document.createElement("div"); |
| container.className = "message bot"; |
| container.style.background = "transparent"; |
| container.style.padding = "0"; |
| |
| const scrollBox = document.createElement("div"); |
| scrollBox.className = "flashcard-container"; |
| |
| // Create Cards |
| cardsData.forEach(card => { |
| const cardDiv = document.createElement("div"); |
| cardDiv.className = "flashcard"; |
| // Flip on click |
| cardDiv.onclick = function() { this.classList.toggle('flipped'); }; |
| |
| cardDiv.innerHTML = ` |
| <div class="flashcard-inner"> |
| <div class="flashcard-front">${card.question}</div> |
| <div class="flashcard-back">${card.answer}</div> |
| </div> |
| `; |
| scrollBox.appendChild(cardDiv); |
| }); |
| |
| container.appendChild(scrollBox); |
| box.appendChild(container); |
| box.scrollTop = box.scrollHeight; |
| } |
| function showTyping() { |
| const box = document.getElementById("chat-box"); |
| const typing = document.createElement("div"); |
| typing.classList.add("message", "bot"); |
| typing.id = "typing"; |
| typing.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Thinking...`; |
| box.appendChild(typing); |
| box.scrollTop = box.scrollHeight; |
| } |
| |
| function removeTyping() { |
| const el = document.getElementById("typing"); |
| if (el) el.remove(); |
| } |
| |
| // --- MIND MAP RENDERER --- |
| async function appendMindMap(mermaidCode) { |
| const box = document.getElementById("chat-box"); |
| const div = document.createElement("div"); |
| div.classList.add("message", "bot"); |
| div.style.background = "white"; |
| div.style.border = "1px solid #ddd"; |
| |
| const id = "mermaid-" + Math.floor(Math.random() * 10000); |
| div.id = id; |
| |
| let cleanCode = mermaidCode.replace(/```mermaid/g, "").replace(/```/g, "").trim(); |
| div.textContent = cleanCode; |
| |
| box.appendChild(div); |
| box.scrollTop = box.scrollHeight; |
| |
| try { |
| await mermaid.init(undefined, div); |
| } catch (error) { |
| div.innerHTML = "β οΈ Could not render diagram."; |
| } |
| } |
| |
| async function sendMessage() { |
| const input = document.getElementById("user-input"); |
| const message = input.value.trim(); |
| if (!message) return; |
| |
| appendMessage(message, "user"); |
| input.value = ""; |
| showTyping(); |
| |
| try { |
| const res = await fetch(API_URL, { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| question: message, |
| session_id: sessionId |
| }) |
| }); |
| |
| const data = await res.json(); |
| removeTyping(); |
| appendMessage(data.answer, "bot"); |
| |
| if (isVoiceActive) { |
| speakText(data.answer); |
| isVoiceActive = false; |
| } |
| |
| } catch (error) { |
| removeTyping(); |
| appendMessage("β οΈ Error contacting server.", "bot"); |
| isVoiceActive = false; |
| } |
| } |
| |
| // --- FEATURE REQUESTS --- |
| async function requestFeature(type) { |
| let topic = ""; |
| let endpoint = ""; |
| let body = {}; |
| let method = "POST"; |
| |
| if (type === 'quiz') { |
| topic = prompt("Enter the topic for the quiz:"); |
| if (!topic) return; |
| appendMessage(`π Generating quiz for: ${topic}...`, "user"); |
| endpoint = "/quiz"; |
| body = JSON.stringify({ topic: topic }); |
| } |
| else if (type === 'summary') { |
| topic = prompt("Enter topic to summarize:"); |
| if (!topic) return; |
| appendMessage(`π Summarizing: ${topic}...`, "user"); |
| endpoint = "/summary"; |
| body = JSON.stringify({ topic: topic }); |
| } |
| else if (type === 'countdown') { |
| appendMessage("β³ Checking exam schedule...", "user"); |
| endpoint = "/countdown"; |
| method = "GET"; |
| } |
| else if (type === 'plan') { |
| const input = prompt("Enter Subject and Days (e.g., 'Science, 3'):"); |
| if (!input) return; |
| const parts = input.split(","); |
| topic = parts[0].trim(); |
| let days = parts.length > 1 ? parts[1].trim() : "5"; |
| appendMessage(`π
Creating ${days}-day plan for ${topic}...`, "user"); |
| endpoint = "/study_plan"; |
| body = JSON.stringify({ subject: topic, days: days }); |
| } |
| else if (type === 'mindmap') { |
| topic = prompt("Enter topic for Mind Map:"); |
| if (!topic) return; |
| appendMessage(`π§ Drawing Mind Map for: ${topic}...`, "user"); |
| endpoint = "/mindmap"; |
| body = JSON.stringify({ topic: topic }); |
| } |
| else if (type === 'flashcards') { |
| topic = prompt("Enter topic for Flashcards:"); |
| if (!topic) return; |
| appendMessage(`π Creating flashcards for: ${topic}...`, "user"); |
| endpoint = "/flashcards"; |
| body = JSON.stringify({ topic: topic }); |
| } |
| else if (type === 'quiz') { |
| renderQuiz(data.answer); |
| } |
| |
| if (!endpoint) return; |
| |
| showTyping(); |
| |
| try { |
| const res = await fetch(`${endpoint}`, { |
| method: method, |
| headers: { "Content-Type": "application/json" }, |
| body: method === "POST" ? body : null |
| }); |
| |
| const data = await res.json(); |
| removeTyping(); |
| |
| if (type === 'mindmap') { |
| appendMindMap(data.answer); |
| } |
| else if (type === 'flashcards') { |
| appendFlashcards(data.answer); |
| } |
| else if (type === 'quiz') { |
| renderQuiz(data.answer); |
| } |
| else { |
| appendMessage(data.answer, "bot"); |
| } |
| |
| } catch (error) { |
| removeTyping(); |
| appendMessage("β οΈ Error fetching data.", "bot"); |
| } |
| } |