Spaces:
Running
Running
/******************************* | |
* Interview Q&A Frontend JS * | |
*******************************/ | |
// Elements | |
const recordBtn = document.getElementById("record-button"); | |
const questionEl = document.getElementById("question-output"); | |
const answerEl = document.getElementById("answer-output"); | |
// Typing animation util | |
function typeEffect(el, text, speed = 30) { | |
el.textContent = ""; | |
let idx = 0; | |
const timer = setInterval(() => { | |
el.textContent += text.charAt(idx); | |
idx++; | |
if (idx >= text.length) clearInterval(timer); | |
}, speed); | |
} | |
// Audio recording setup | |
let mediaRecorder, chunks = []; | |
// Initialise media data | |
async function initMedia() { | |
try { | |
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
mediaRecorder = new MediaRecorder(stream); | |
// Continuously push available data | |
mediaRecorder.ondataavailable = e => chunks.push(e.data); | |
// Stop btn start write and send audio chunk | |
mediaRecorder.onstop = async () => { | |
const audioBlob = new Blob(chunks, { type: "audio/wav" }); | |
chunks = []; | |
// Build form data | |
const form = new FormData(); | |
form.append("file", audioBlob, "record.wav"); | |
// UX feedback | |
typeEffect(questionEl, "⌛ Transcribing…"); | |
answerEl.textContent = ""; | |
try { | |
const res = await fetch("/voice-transcribe", { method: "POST", body: form }); | |
if (!res.ok) throw new Error(`HTTP ${res.status}`); | |
const data = await res.json(); | |
// render markdown after a small delay for dramatic effect | |
typeEffect(questionEl, data.question || "[no speech detected]"); | |
setTimeout(() => typeEffect(answerEl, data.answer || "[no answer]"), 500); | |
} catch (err) { | |
typeEffect(answerEl, "❌ " + err.message); | |
} | |
}; | |
} catch (err) { | |
alert("Microphone access denied – please allow permissions."); | |
} | |
} | |
// Hold‑to‑record UX | |
function bindRecordBtn() { | |
if (!mediaRecorder) return; | |
recordBtn.addEventListener("mousedown", () => mediaRecorder.start()); | |
recordBtn.addEventListener("mouseup", () => mediaRecorder.stop()); | |
// Touch devices | |
recordBtn.addEventListener("touchstart", e => { e.preventDefault(); mediaRecorder.start(); }); | |
recordBtn.addEventListener("touchend", e => { e.preventDefault(); mediaRecorder.stop(); }); | |
} | |
// Init on page load | |
window.addEventListener("DOMContentLoaded", async () => { | |
try { await initMedia(); bindRecordBtn(); } | |
catch (e) { alert("Mic permission required"); } | |
}); |