Spaces:
Runtime error
Runtime error
Download static/script.js from Darshankumarr03/Swift-Query: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/spaces/Darshankumarr03/Swift-Query/resolve/main/static/script.js
- Command line
-
hf download hf://spaces/Darshankumarr03/Swift-Query/static/script.js
-
curl -L -o script.js https://huggingface.co/spaces/Darshankumarr03/Swift-Query/resolve/main/static/script.js
15.7 kB
| document.addEventListener("DOMContentLoaded", () => { | |
| // --- Element Selectors --- | |
| const userInput = document.getElementById("user-input"); | |
| const sendButton = document.getElementById("send-button"); | |
| const chatHistory = document.getElementById("chat-history"); | |
| const menuItems = document.querySelectorAll(".menu-item"); | |
| const convoRecord = document.getElementById("convo-record"); | |
| // Modal Elements | |
| const patientModal = document.getElementById("patient-modal"); | |
| const patientForm = document.getElementById("patient-form"); | |
| const patientIdInput = document.getElementById("patient-id-input"); | |
| // Track session for backend context | |
| let currentSessionId = window.activeSessionId || null; | |
| let similarSearchActive = false; // --- NEW: Toggle state for similar search | |
| // --- NEW: Function to control chat input state --- | |
| function setChatInputDisabled(disabled) { | |
| if (userInput) { | |
| userInput.disabled = disabled; | |
| userInput.placeholder = disabled ? "Please start a patient session first..." : "Ask Swift Query AI..."; | |
| } | |
| if (sendButton) { | |
| sendButton.disabled = disabled; | |
| sendButton.classList.toggle('bg-gray-400', disabled); | |
| sendButton.classList.toggle('bg-primary', !disabled); | |
| sendButton.classList.toggle('hover:bg-orange-600', !disabled); | |
| } | |
| } | |
| // --- Feature Handlers (MODIFIED) --- | |
| const featureHandlers = { | |
| "chat-feature": () => { | |
| if (similarSearchActive) { | |
| // If similar search was active, turn it off | |
| toggleSimilarSearch(document.getElementById('similar-cases-feature')); | |
| } | |
| if (userInput) userInput.focus(); | |
| }, | |
| "similar-cases-feature": (event) => { | |
| if (!window.activePatientId) { | |
| appendMessage("Please start a patient session to find similar cases.", "ai", new Date()); | |
| return; | |
| } | |
| // NEW: Toggle the similar search mode | |
| toggleSimilarSearch(event.currentTarget); | |
| }, | |
| "patient-search-feature": () => { | |
| if (patientModal) { | |
| patientModal.classList.remove("hidden"); | |
| if (patientIdInput) setTimeout(() => patientIdInput.focus(), 100); | |
| } | |
| }, | |
| "clear-chat-feature": () => { | |
| const greeting = chatHistory.firstChild; | |
| chatHistory.innerHTML = ''; | |
| if(greeting) chatHistory.appendChild(greeting); | |
| appendMessage("Local chat view cleared.", "ai", new Date()); | |
| }, | |
| "history-feature": () => { | |
| loadHistoryList(); | |
| } | |
| }; | |
| // --- NEW: Toggle function for Similar Search --- | |
| function toggleSimilarSearch(buttonElement) { | |
| similarSearchActive = !similarSearchActive; | |
| if (!userInput || !buttonElement) return; | |
| if (similarSearchActive) { | |
| userInput.value = "/similar "; | |
| userInput.focus(); | |
| buttonElement.classList.add("active"); // Add visual indicator | |
| appendMessage("**Similar Case Search Activated.**\nPlease describe the symptoms or conditions to find matching cases.", "ai", new Date()); | |
| } else { | |
| if (userInput.value.startsWith("/similar ")) { | |
| userInput.value = ""; | |
| } | |
| buttonElement.classList.remove("active"); | |
| appendMessage("**Similar Case Search Deactivated.**", "ai", new Date()); | |
| } | |
| } | |
| // --- Event Listeners --- | |
| menuItems.forEach(item => { | |
| if (item.tagName === 'A' && !item.href.includes('logout')) { | |
| item.addEventListener("click", (e) => { | |
| if (featureHandlers[item.id]) { | |
| e.preventDefault(); | |
| // Don't deactivate similar search button when it's clicked | |
| if (item.id !== 'similar-cases-feature') { | |
| const activeItem = document.querySelector(".menu-item.active"); | |
| if (activeItem) activeItem.classList.remove("active"); | |
| item.classList.add("active"); | |
| } | |
| featureHandlers[item.id](e); | |
| } | |
| }); | |
| } | |
| }); | |
| if (patientForm) { | |
| patientForm.addEventListener("submit", (e) => { | |
| e.preventDefault(); | |
| const pid = patientIdInput.value.trim(); | |
| if (pid) { | |
| if (patientModal) patientModal.classList.add("hidden"); | |
| startPatientSession(pid); | |
| } | |
| }); | |
| } | |
| // --- Recording Logic --- | |
| let mediaRecorder = null; | |
| let audioChunks = []; | |
| window.startRecording = async function () { | |
| if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) return; | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| mediaRecorder = new MediaRecorder(stream); | |
| audioChunks = []; | |
| mediaRecorder.ondataavailable = (event) => { | |
| if (event.data.size > 0) audioChunks.push(event.data); | |
| }; | |
| mediaRecorder.onstop = async () => { | |
| const audioBlob = new Blob(audioChunks, { type: 'audio/mp3' }); | |
| appendMessage("Processing recording... ⏳", "ai", new Date()); | |
| const formData = new FormData(); | |
| formData.append("audio", audioBlob, "recording.mp3"); | |
| formData.append("patient_id", window.activePatientId); | |
| try { | |
| const response = await fetch("/doctor/upload_recording", { method: "POST", body: formData }); | |
| const result = await response.json(); | |
| if (result.success) { | |
| appendMessage(`### 🎙️ Recording Saved\n\n**Processed Note:**\n${result.data.processed_note}`, "ai", new Date()); | |
| } else { | |
| appendMessage(`❌ Recording failed: ${result.error}`, "ai", new Date()); | |
| } | |
| } catch (err) { | |
| console.error(err); | |
| appendMessage("❌ Error uploading recording.", "ai", new Date()); | |
| } | |
| stream.getTracks().forEach(track => track.stop()); | |
| }; | |
| mediaRecorder.start(); | |
| appendMessage("🎙️ **Recording Started...**", "ai", new Date()); | |
| if (convoRecord) convoRecord.checked = true; | |
| } catch (err) { | |
| console.error("Microphone access denied:", err); | |
| if (convoRecord) convoRecord.checked = false; | |
| alert("Microphone access is required to record."); | |
| } | |
| }; | |
| window.stopRecording = function () { | |
| if (mediaRecorder && mediaRecorder.state !== "inactive") { | |
| mediaRecorder.stop(); | |
| appendMessage("⏹️ **Recording Stopped.** Processing...", "ai", new Date()); | |
| if (convoRecord) convoRecord.checked = false; | |
| } | |
| }; | |
| if (convoRecord) { | |
| convoRecord.addEventListener("change", async () => { | |
| if (!window.activePatientId) { | |
| convoRecord.checked = false; | |
| alert("Please start a patient session before starting a recording."); | |
| return; | |
| } | |
| if (convoRecord.checked) await window.startRecording(); | |
| else window.stopRecording(); | |
| }); | |
| } | |
| // --- Core Chat Functions --- | |
| if (sendButton) sendButton.addEventListener("click", sendMessage); | |
| if (userInput) { | |
| userInput.addEventListener("keypress", (e) => { | |
| if (e.key === "Enter" && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }); | |
| userInput.addEventListener("input", function () { | |
| this.style.height = "50px"; | |
| this.style.height = `${Math.min(this.scrollHeight, 200)}px`; | |
| }); | |
| } | |
| function startPatientSession(patientId) { | |
| setChatInputDisabled(true); | |
| if (chatHistory) chatHistory.innerHTML = ''; | |
| appendMessage(`Starting session for Patient ID: ${patientId}... This may take a moment.`, "ai", new Date()); | |
| fetch(`/doctor/start_session/${patientId}`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| }) | |
| .then(res => res.json()) | |
| .then(data => { | |
| if (data.success) { | |
| appendMessage(`Session for patient ${data.patient_id} is ready. Reloading dashboard...`, "ai", new Date()); | |
| setTimeout(() => window.location.reload(), 500); | |
| } else { | |
| console.error("Error starting session:", data.error); | |
| appendMessage(`Failed to start session: ${data.error}`, "ai", new Date()); | |
| setChatInputDisabled(false); | |
| } | |
| }) | |
| .catch(err => { | |
| console.error("Network error starting session:", err); | |
| appendMessage("A network error occurred while starting the session.", "ai", new Date()); | |
| setChatInputDisabled(false); | |
| }); | |
| } | |
| function loadHistoryList() { | |
| chatHistory.innerHTML = ''; | |
| appendMessage("Loading history sessions...", "ai", new Date()); | |
| fetch("/api/history") | |
| .then(res => res.json()) | |
| .then(data => { | |
| chatHistory.innerHTML = ''; | |
| if (data.history && data.history.length > 0) { | |
| appendMessage("### Select a Past Session to Resume:", "ai", new Date()); | |
| const list = document.createElement("div"); | |
| list.className = "flex flex-col gap-2 mt-2"; | |
| data.history.forEach(item => { | |
| const btn = document.createElement("button"); | |
| btn.className = "bg-white p-3 rounded-lg shadow-sm hover:bg-orange-50 text-left border border-gray-200 transition-colors w-full group"; | |
| btn.innerHTML = `<div class='font-bold text-primary flex justify-between'><span>${item.title || 'Untitled'}</span><i class="fas fa-chevron-right text-gray-300 group-hover:text-primary transition-colors"></i></div><div class='text-xs text-gray-500 mt-1'><i class="far fa-clock mr-1"></i> ${item.display_time || 'Unknown date'}</div>`; | |
| btn.onclick = () => window.loadHistoryItem(item.type || 'chat', item.session_id); | |
| list.appendChild(btn); | |
| }); | |
| const container = document.createElement("div"); | |
| container.className = "w-full max-w-lg mb-4"; | |
| container.appendChild(list); | |
| chatHistory.appendChild(container); | |
| chatHistory.scrollTop = chatHistory.scrollHeight; | |
| } else { | |
| appendMessage("No past sessions found.", "ai", new Date()); | |
| } | |
| }) | |
| .catch(err => { | |
| console.error(err); | |
| appendMessage("Failed to load history.", "ai", new Date()); | |
| }); | |
| } | |
| window.loadHistoryItem = function (type, id) { | |
| chatHistory.innerHTML = ''; | |
| appendMessage("Restoring...", "ai", new Date()); | |
| currentSessionId = (type === 'chat') ? id : null; | |
| fetch(`/api/history/${type}/${id}`) | |
| .then(res => res.json()) | |
| .then(data => { | |
| chatHistory.innerHTML = ''; | |
| if (data.messages && data.messages.length > 0) { | |
| data.messages.forEach(msg => appendMessage(msg.content, msg.sender, msg.timestamp || new Date())); | |
| if (type === 'chat') appendMessage("--- Session Restored ---", "ai", new Date()); | |
| } else { | |
| appendMessage("Item is empty.", "ai", new Date()); | |
| } | |
| }) | |
| .catch(err => { | |
| console.error(err); | |
| appendMessage("Failed to restore item.", "ai", new Date()); | |
| }); | |
| }; | |
| function sendMessage() { | |
| if (!userInput || userInput.disabled) return; | |
| const message = userInput.value.trim(); | |
| if (message === "") return; | |
| appendMessage(message, "user", new Date()); | |
| userInput.value = ""; | |
| userInput.style.height = "50px"; | |
| fetchAIResponse(message); | |
| // NEW: Deactivate similar search after sending the query | |
| if (similarSearchActive) { | |
| toggleSimilarSearch(document.getElementById('similar-cases-feature')); | |
| } | |
| } | |
| async function fetchAIResponse(message) { | |
| if (!window.activePatientId || !currentSessionId) { | |
| appendMessage("Cannot send message. No active patient session.", "ai", new Date()); | |
| return; | |
| } | |
| try { | |
| const response = await fetch("/api/chat", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ message: message, session_id: currentSessionId }), | |
| }); | |
| const data = await response.json(); | |
| appendMessage(data.response, "ai", new Date()); | |
| } catch (error) { | |
| console.error("Error fetching AI response:", error); | |
| appendMessage("Sorry, I'm having trouble connecting to the AI service.", "ai", new Date()); | |
| } | |
| } | |
| function appendMessage(message, sender, timestamp) { | |
| if (!chatHistory) return; | |
| const messageWrapper = document.createElement("div"); | |
| messageWrapper.classList.add("flex", "flex-col", sender === "user" ? "items-end" : "items-start", "mb-4", "animate-fade-in"); | |
| const bubble = document.createElement("div"); | |
| bubble.classList.add("rounded-lg", "p-3", "max-w-lg", "shadow-sm", "text-sm", "md:text-base"); | |
| if (sender === "user") { | |
| bubble.classList.add("bg-primary", "text-white"); | |
| bubble.innerText = message; | |
| } else { | |
| bubble.classList.add("bg-white", "text-text-dark", "prose", "prose-orange", "prose-sm"); | |
| const rawHtml = marked.parse(message); | |
| bubble.innerHTML = DOMPurify.sanitize(rawHtml); | |
| } | |
| let timeStr = ""; | |
| try { | |
| if (timestamp instanceof Date) { | |
| timeStr = timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| } else if (typeof timestamp === 'string') { | |
| timeStr = new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| } | |
| } catch (e) { /* Ignore */ } | |
| const meta = document.createElement("div"); | |
| meta.classList.add("text-xs", "text-gray-400", "mt-1", "mx-1"); | |
| meta.innerText = timeStr; | |
| messageWrapper.appendChild(bubble); | |
| messageWrapper.appendChild(meta); | |
| chatHistory.appendChild(messageWrapper); | |
| chatHistory.scrollTop = chatHistory.scrollHeight; | |
| } | |
| // --- INITIALIZATION on Page Load --- | |
| if (window.activePatientId) { | |
| console.log(`Session active for patient ${window.activePatientId}`); | |
| setChatInputDisabled(false); // ENABLE chat | |
| if (userInput) userInput.focus(); | |
| if (window.activeSessionId && chatHistory.children.length <= 1) { | |
| console.log("Loading active session history:", window.activeSessionId); | |
| loadHistoryItem('chat', window.activeSessionId); | |
| } | |
| } else { | |
| console.log("No active patient session."); | |
| setChatInputDisabled(true); // DISABLE chat by default | |
| } | |
| }); |