/** * @name chat.js * @version 0.1.4 * @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); autoContinue(); } 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; }; } function autoContinue() { // Create an instance of the observer const observer = new MutationObserver((mutationsList) => { for (let mutation of mutationsList) { if (mutation.type === 'childList') { const btn = Array.from(mutation.target.querySelectorAll('button.btn')).find((btn) => btn.innerText.includes('Continue generating'), ); if (btn) { console.log("Found the button of 'Continue generating'"); setTimeout(() => { console.log('Clicked it to continue generating after 1 second'); btn.click(); }, 1000); return; } } } }); // Wait until the form exists const interval = setInterval(() => { if (document.forms[0]) { // Start observing the dom change of the form observer.observe(document.forms[0], { attributes: false, childList: true, subtree: true, }); clearInterval(interval); // Stop checking when the form exists } }, 1000); } init(); } document.addEventListener('DOMContentLoaded', chatInit);