Spaces:
Running
Running
| function typewriterEffect(text, speed = 50, enableTTS = true) { | |
| agentOutput.textContent = ''; | |
| agentOutput.classList.add('active'); | |
| agentOutput.classList.remove('typing-done'); | |
| let index = 0; | |
| if (typingInterval) { | |
| clearInterval(typingInterval); | |
| } | |
| if (enableTTS && typeof speakText === 'function') { | |
| speakText(text); // 語音與打字效果並行 | |
| } | |
| typingInterval = setInterval(() => { | |
| if (index < text.length) { | |
| agentOutput.textContent += text[index]; | |
| index++; | |
| } else { | |
| clearInterval(typingInterval); | |
| agentOutput.classList.add('typing-done'); // 打字完成,隱藏游標 | |
| } | |
| }, speed); | |
| } | |
| function hideAgentOutput() { | |
| if (typingInterval) { | |
| clearInterval(typingInterval); | |
| } | |
| agentOutput.classList.remove('active'); | |
| agentOutput.textContent = ''; | |
| } | |
| function initEmotionSelector() { | |
| document.getElementById('emotion-select').addEventListener('change', (e) => { | |
| const emotion = e.target.value; | |
| background.className = `voice-immersive-background emotion-${emotion} active`; | |
| emotionIndicator.textContent = `當前情緒: ${emotionEmojis[emotion]}`; | |
| }); | |
| } | |
| function initTranscriptControls() { | |
| document.getElementById('transcript-provisional').addEventListener('click', () => { | |
| transcript.textContent = '今天天氣怎麼樣'; | |
| transcript.className = 'voice-transcript provisional'; | |
| }); | |
| document.getElementById('transcript-final').addEventListener('click', () => { | |
| transcript.textContent = '今天天氣怎麼樣?'; | |
| transcript.className = 'voice-transcript final'; | |
| }); | |
| } | |
| function initLoginButton() { | |
| const googleLoginBtn = document.getElementById('googleLoginBtn'); | |
| if (googleLoginBtn) { | |
| googleLoginBtn.addEventListener('click', handleGoogleLogin); | |
| } | |
| } | |
| function initLogoutButton() { | |
| const logoutBtn = document.getElementById('logoutBtn'); | |
| if (logoutBtn) { | |
| logoutBtn.addEventListener('click', handleLogout); | |
| } | |
| } | |
| function handleLogout() { | |
| localStorage.removeItem('jwt_token'); | |
| if (typeof ws !== 'undefined' && ws) { | |
| ws.close(); | |
| } | |
| if (typeof stopSpeaking === 'function') { | |
| stopSpeaking(); | |
| } | |
| window.location.href = '/login/'; | |
| } | |
| let isTextInputMode = false; // 當前是否為文字輸入模式 | |
| let textInputElement = null; // 文字輸入框元素 | |
| function initChatIcon() { | |
| const chatIcon = document.getElementById('chatIcon'); | |
| if (chatIcon) { | |
| chatIcon.addEventListener('click', toggleInputMode); | |
| } | |
| } | |
| function toggleInputMode() { | |
| isTextInputMode = !isTextInputMode; | |
| const transcript = document.getElementById('transcript'); | |
| if (!transcript) { | |
| console.error('❌ 找不到 transcript 元素'); | |
| return; | |
| } | |
| if (isTextInputMode) { | |
| const originalContent = transcript.textContent; | |
| transcript.className = 'voice-transcript text-input-mode'; | |
| transcript.innerHTML = ''; | |
| textInputElement = document.createElement('textarea'); | |
| textInputElement.placeholder = '請輸入訊息...'; | |
| textInputElement.id = 'text-input-box'; | |
| textInputElement.addEventListener('keydown', handleTextInput); | |
| transcript.appendChild(textInputElement); | |
| setTimeout(() => textInputElement.focus(), 100); | |
| } else { | |
| if (textInputElement) { | |
| textInputElement.removeEventListener('keydown', handleTextInput); | |
| textInputElement = null; | |
| } | |
| transcript.className = 'voice-transcript provisional'; | |
| transcript.textContent = '請說話...'; | |
| } | |
| } | |
| function handleTextInput(event) { | |
| if (event.key === 'Enter' && !event.shiftKey) { | |
| event.preventDefault(); | |
| const text = textInputElement.value.trim(); | |
| if (!text) { | |
| console.warn('⚠️ 訊息為空,不送出'); | |
| return; | |
| } | |
| if (typeof wsManager !== 'undefined' && wsManager) { | |
| const chatId = window.currentChatId || null; | |
| wsManager.sendUserMessage(text, chatId); | |
| textInputElement.value = ''; | |
| if (typeof setState === 'function') { | |
| setState('thinking'); | |
| } | |
| toggleInputMode(); | |
| } else { | |
| console.error('❌ WebSocket 未初始化'); | |
| } | |
| } | |
| } | |