/** * @name chat.js * @version 0.1.0 * @url https://github.com/lencx/ChatGPT/tree/main/scripts/chat.js */ function chatInit() { const ICONS = { copy: ``, cpok: ``, voice: ``, speaking: ``, }; let currentUtterance = null; let currentIndex = -1; let chatConf = {}; async function init() { chatConf = (await invoke('get_app_conf')) || {}; new MutationObserver(observeMutations).observe(document.body, { childList: true, subtree: true, }); document.addEventListener('visibilitychange', focusOnInput); } function observeMutations(mutationsList) { for (const mutation of mutationsList) { if (mutation.target.closest('form')) { addChatButtons(); } } } function focusOnInput() { const textArea = document.getElementsByTagName('textarea')[0]; if (textArea) { textArea.focus(); } } function addChatButtons() { const list = Array.from(document.querySelectorAll('main >div>div>div>div>div')); list.forEach((item, idx) => { if (shouldSkip(item)) { return; } const saybtn = item.querySelector('button.rounded-md').cloneNode(true); saybtn.classList.add('chat-item-voice'); saybtn.title = 'Say'; saybtn.innerHTML = ICONS.voice; item.querySelector('.self-end').appendChild(saybtn); saybtn.onclick = () => handleClick(item, idx, saybtn); }); } function shouldSkip(item) { return ( item.querySelector('.chat-item-voice') || !item.querySelector('button.rounded-md') || !item.querySelector('.self-end') ); } function handleClick(item, idx, saybtn) { const synth = window.speechSynthesis; const list = Array.from(document.querySelectorAll('main >div>div>div>div>div')); if (currentUtterance && currentIndex !== -1) { synth.cancel(); if (idx === currentIndex) { saybtn.innerHTML = ICONS.voice; currentUtterance = null; currentIndex = -1; return; } else if (list[currentIndex].querySelector('.chat-item-voice')) { list[currentIndex].querySelector('.chat-item-voice').innerHTML = ICONS.voice; list[idx].querySelector('.chat-item-voice').innerHTML = ICONS.speaking; } } const txt = item?.innerText?.trim() || ''; if (!txt) return; const utterance = new SpeechSynthesisUtterance(txt); const voices = speechSynthesis.getVoices(); let voice = voices.find((voice) => voice.voiceURI === chatConf.speech_lang); if (!voice) { voice = voices.find((voice) => voice.lang === 'en-US'); } utterance.voice = voice; currentIndex = idx; utterance.lang = voice.lang; synth.speak(utterance); saybtn.innerHTML = ICONS.speaking; currentUtterance = utterance; currentIndex = idx; utterance.onend = () => { saybtn.innerHTML = ICONS.voice; currentUtterance = null; currentIndex = -1; }; } } if (document.readyState === 'complete' || document.readyState === 'interactive') { chatInit(); } else { document.addEventListener('DOMContentLoaded', chatInit); }