diff --git a/src/tts-modal.js b/src/tts-modal.js index b9a34b4..270ed7a 100644 --- a/src/tts-modal.js +++ b/src/tts-modal.js @@ -283,29 +283,203 @@ export function openTTSModal() { resultDiv.style.display = "block"; resultDiv.innerHTML = ""; + // Create hidden audio element (no controls) const audio = new Audio(result.blobUrl); - audio.controls = true; - audio.style.width = "100%"; - resultDiv.appendChild(audio); + audio.preload = "auto"; + + // Create custom control container + const controlsContainer = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + controlsContainer.style.cssText = ` + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background: white; + border-radius: 8px; + border: 1px solid #ddd; + margin-bottom: 12px; + `; + } else { + controlsContainer.style.cssText = ` + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + margin-bottom: 12px; + `; + } + // Create play/pause button + const playPauseBtn = document.createElement("button"); + playPauseBtn.textContent = "⏸️ Pause"; + if (USE_CUSTOM_STYLING) { + playPauseBtn.style.cssText = ` + padding: 8px 16px; + background: #2196F3; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + min-width: 100px; + `; + } else { + playPauseBtn.style.cssText = ` + padding: 8px 16px; + min-width: 100px; + `; + } + + // Create progress bar + const progressContainer = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + progressContainer.style.cssText = ` + flex: 1; + height: 6px; + background: #e0e0e0; + border-radius: 3px; + position: relative; + cursor: pointer; + `; + } else { + progressContainer.style.cssText = ` + flex: 1; + height: 6px; + background: var(--border-color, #e0e0e0); + border-radius: 3px; + position: relative; + cursor: pointer; + `; + } + + const progressBar = document.createElement("div"); + if (USE_CUSTOM_STYLING) { + progressBar.style.cssText = ` + height: 100%; + background: #2196F3; + border-radius: 3px; + width: 0%; + transition: width 0.1s; + `; + } else { + progressBar.style.cssText = ` + height: 100%; + background: var(--accent-color, #2196F3); + border-radius: 3px; + width: 0%; + transition: width 0.1s; + `; + } + progressContainer.appendChild(progressBar); + + // Create time display + const timeDisplay = document.createElement("span"); + timeDisplay.textContent = "0:00 / 0:00"; + if (USE_CUSTOM_STYLING) { + timeDisplay.style.cssText = ` + font-size: 12px; + color: #666; + min-width: 80px; + text-align: center; + `; + } + + // Create download button const downloadBtn = document.createElement("button"); - downloadBtn.textContent = "💾 Download Audio"; + downloadBtn.textContent = "💾 Download"; if (USE_CUSTOM_STYLING) { downloadBtn.style.cssText = ` - margin-top: 8px; - padding: 8px 16px; - background: #4CAF50; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - `; + padding: 8px 16px; + background: #4CAF50; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + `; + } else { + downloadBtn.style.cssText = ` + padding: 8px 16px; + `; } downloadBtn.onclick = () => downloadAudio(result.blobUrl, "tts-audio.mp3"); - resultDiv.appendChild(downloadBtn); - // Auto-play - audio.play(); + // Assemble controls + controlsContainer.appendChild(playPauseBtn); + controlsContainer.appendChild(progressContainer); + controlsContainer.appendChild(timeDisplay); + controlsContainer.appendChild(downloadBtn); + resultDiv.appendChild(controlsContainer); + + // Audio event handlers + let isPlaying = false; + + const formatTime = (seconds) => { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + const updateProgress = () => { + if (audio.duration) { + const progress = (audio.currentTime / audio.duration) * 100; + progressBar.style.width = `${progress}%`; + timeDisplay.textContent = `${formatTime(audio.currentTime)} / ${formatTime(audio.duration)}`; + } + }; + + playPauseBtn.onclick = () => { + if (isPlaying) { + audio.pause(); + playPauseBtn.textContent = "▶️ Play"; + isPlaying = false; + } else { + audio.play(); + playPauseBtn.textContent = "⏸️ Pause"; + isPlaying = true; + } + }; + + // Progress bar click handler + progressContainer.onclick = (e) => { + if (audio.duration) { + const rect = progressContainer.getBoundingClientRect(); + const clickX = e.clientX - rect.left; + const clickRatio = clickX / rect.width; + audio.currentTime = clickRatio * audio.duration; + } + }; + + // Audio event listeners + audio.addEventListener('loadedmetadata', () => { + timeDisplay.textContent = `0:00 / ${formatTime(audio.duration)}`; + }); + + audio.addEventListener('timeupdate', updateProgress); + + audio.addEventListener('ended', () => { + playPauseBtn.textContent = "▶️ Replay"; + isPlaying = false; + progressBar.style.width = '0%'; + audio.currentTime = 0; + }); + + audio.addEventListener('error', (e) => { + console.error('Audio playback error:', e); + playPauseBtn.textContent = "❌ Error"; + playPauseBtn.disabled = true; + }); + + // Auto-play and set initial state + audio.play().then(() => { + isPlaying = true; + playPauseBtn.textContent = "⏸️ Pause"; + }).catch((e) => { + console.warn('Auto-play failed, user interaction required:', e); + isPlaying = false; + playPauseBtn.textContent = "▶️ Play"; + }); } } catch (error) { alert(getUIText("ttsFailed", { error: error.message }));