Spaces:
Sleeping
Sleeping
File size: 2,527 Bytes
e7b1e22 243b6fb e7b1e22 243b6fb e7b1e22 243b6fb e7b1e22 33e4e84 e7b1e22 243b6fb e7b1e22 243b6fb e7b1e22 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
/*******************************
* 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"); }
}); |