import { state } from '../core/state.js'; import { logTts, prefetchAudio, speak, stopAllAudio } from '../services/tts.js'; let autoPlayActive = false; let autoPlayTimer = null; export function toggleAutoPlay() { if (autoPlayActive) stopAutoPlay(); else startAutoPlay(); } export function startAutoPlay() { const rows = document.querySelectorAll('.word-table tbody tr[data-word-id]'); const wordIds = Array.from(rows).map(row => Number.parseInt(row.dataset.wordId, 10)); if (wordIds.length === 0) return; autoPlayActive = true; updateAutoPlayBtn(); prefetchWordListAudio(wordIds); playWordSequence(wordIds, 0); } export function prefetchWordListAudio(wordIds) { const texts = wordIds .map(id => state.words.find(word => word.id === id)?.english) .filter(Boolean); if (!texts.length) return; void prefetchAudio(texts, 0.92, { allowCloud: true }) .catch(error => logTts('word list audio prefetch failed', error)); } export function playWordSequence(wordIds, index) { if (!autoPlayActive || index >= wordIds.length) { stopAutoPlay(); return; } const word = state.words.find(item => item.id === wordIds[index]); if (!word) { playWordSequence(wordIds, index + 1); return; } document.querySelectorAll('.word-table tbody tr').forEach(row => row.classList.remove('is-playing')); const row = document.querySelector(`tr[data-word-id="${word.id}"]`); if (row) { row.classList.add('is-playing'); let shouldScroll = true; if (state.autoPlayScrollMode === 'visible') { const rect = row.getBoundingClientRect(); const viewportHeight = window.innerHeight || document.documentElement.clientHeight; const header = document.getElementById('mobile-header'); const topBound = header ? header.getBoundingClientRect().height : 0; shouldScroll = rect.top < topBound || rect.bottom > viewportHeight; } if (shouldScroll) { row.scrollIntoView({ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth', block: 'center', inline: 'nearest' }); } } const repeatCount = Math.max(1, state.autoPlayRepeat || 1); const repeatDelay = state.autoPlayRepeatDelay || 1000; const wordInterval = state.autoPlayInterval || 2500; let spoken = 0; async function playCurrentWord() { if (!autoPlayActive) return; await speak(word.english); if (!autoPlayActive) return; spoken++; if (spoken < repeatCount) { autoPlayTimer = setTimeout(playCurrentWord, repeatDelay); } else { autoPlayTimer = setTimeout(() => playWordSequence(wordIds, index + 1), wordInterval); } } void playCurrentWord(); } export function stopAutoPlay() { autoPlayActive = false; if (autoPlayTimer) { clearTimeout(autoPlayTimer); autoPlayTimer = null; } stopAllAudio(); document.querySelectorAll('.word-table tbody tr').forEach(row => row.classList.remove('is-playing')); updateAutoPlayBtn(); } export function updateAutoPlayBtn() { const button = document.getElementById('auto-play-btn'); if (!button) return; if (autoPlayActive) { button.innerHTML = ' 停止播放'; button.className = 'btn btn-error'; } else { button.innerHTML = ' 自动播放'; button.className = 'btn btn-secondary'; } }