diff --git a/public/js/app.js b/public/js/app.js index b2db828..fc4bf73 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -117,18 +117,41 @@ function showToast(message, type) { function copyText(elementId) { var el = document.getElementById(elementId); if (!el) return; - var text = el.innerText || el.textContent; - navigator.clipboard.writeText(text).then(function() { - showToast('Copied!', 'success'); - }).catch(function() { - var range = document.createRange(); - range.selectNode(el); - window.getSelection().removeAllRanges(); - window.getSelection().addRange(range); - document.execCommand('copy'); - window.getSelection().removeAllRanges(); - showToast('Copied!', 'success'); - }); + var text = el.innerText || el.textContent || ''; + if (!text.trim()) { showToast('Nothing to copy', 'error'); return; } + + // Try modern clipboard API (requires HTTPS or localhost) + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(function() { + showToast('Copied!', 'success'); + }).catch(function(err) { + // Fallback: select + execCommand + try { + var range = document.createRange(); + range.selectNodeContents(el); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + var ok = document.execCommand('copy'); + window.getSelection().removeAllRanges(); + showToast(ok ? 'Copied!' : 'Copy failed — please select text manually', ok ? 'success' : 'error'); + } catch (e) { + showToast('Copy not supported in this browser', 'error'); + } + }); + } else { + // No clipboard API — use execCommand directly + try { + var range = document.createRange(); + range.selectNodeContents(el); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + var ok = document.execCommand('copy'); + window.getSelection().removeAllRanges(); + showToast(ok ? 'Copied!' : 'Copy failed — please select text manually', ok ? 'success' : 'error'); + } catch (e) { + showToast('Copy not supported in this browser', 'error'); + } + } } // Speak / Stop @@ -142,41 +165,17 @@ function speakText(elementId) { if (!el) return; var text = (el.innerText || el.textContent).trim(); if (!text) { showToast('Nothing to read', 'error'); return; } - + if (!('speechSynthesis' in window)) { showToast('TTS not supported', 'error'); return; } + window.speechSynthesis.cancel(); + var utter = new SpeechSynthesisUtterance(text); + utter.rate = 0.9; + utter.onend = function() { stopReading(); }; + utter.onerror = function() { stopReading(); }; currentlyReadingId = elementId; var btn = findReadButton(elementId); if (btn) { btn.classList.add('btn-reading'); btn.innerHTML = ' Stop'; } - - // Try ElevenLabs backend first - var token = window.AUTH_TOKEN || localStorage.getItem('ped_scribe_token') || ''; - fetch('/api/text-to-speech', { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, - body: JSON.stringify({ text: text }) - }) - .then(function(r) { - if (!r.ok) throw new Error('not configured'); - return r.blob(); - }) - .then(function(blob) { - var url = URL.createObjectURL(blob); - currentAudio = new Audio(url); - currentAudio.onended = function() { URL.revokeObjectURL(url); stopReading(); }; - currentAudio.onerror = function() { URL.revokeObjectURL(url); stopReading(); }; - currentAudio.play(); - showToast('Reading — click Stop to end', 'info'); - }) - .catch(function() { - // Fallback to native speech synthesis - if (!('speechSynthesis' in window)) { stopReading(); showToast('TTS not supported', 'error'); return; } - window.speechSynthesis.cancel(); - var utter = new SpeechSynthesisUtterance(text); - utter.rate = 0.9; - utter.onend = function() { stopReading(); }; - utter.onerror = function() { stopReading(); }; - window.speechSynthesis.speak(utter); - showToast('Reading — click Stop to end', 'info'); - }); + window.speechSynthesis.speak(utter); + showToast('Reading — click Stop to end', 'info'); } function stopReading() {