Hello everyone!
I was working on a fun little side project trying to get realtime voice to work and I thought I’d share it to anyone who is also interested!
What it does
This custom code allows for users to automatically hear agent responses and speak to the agent without having to activate their microphone every time. To start the conversation, turn on your mic and speak to it. After each conversation, just press enter to send your message. The agent will respond and the text will be automatically read out loud by whichever voice you chose. After the agent is done, your mic will automatically turn back on again!
Some limitations
This custom code will only work for the Google Chrome browser and can only transcribe English speech to text.
How to install it
- Go to one of your Pickaxe direct link deployments and edit it
- Scroll down to the Custom Code section
- Paste the script below into the Body field.
- Save your changes
And that should be it! Here’s the custom code that I used for this cool feature!(Sorry it looks weird! I had to minify the code to get it within the character count for a post!)
<script>
(function(){if(window.__pickaxeVoiceLoopInstalled)return;window.__pickaxeVoiceLoopInstalled=true;var PICKAXE_API2_BASE="https://pickaxe-api2.pickaxe.co";var AZURE_SPEECH_REGION="eastus";var PICKAXE_READOUT_PROVIDER=null;var PICKAXE_ID="";var PICKAXE_READOUT_VOICE=null;var PICKAXE_READOUT_LOCALE="en-US";var VOICE_INPUT_LANG="en-US";var AUTO_TURN_MIC_ON_AFTER_AGENT_RESPONSE=true;var TTS_DEBOUNCE_MS=900;var RESTART_LISTENING_DELAY_MS=350;var SpeechRecognition=window.SpeechRecognition||window.webkitSpeechRecognition;var lastSpokenText="";var ttsDebounceTimer=null;var restartTimer=null;var sdkPromise=null;var activeSynthesizer=null;var activeSpeaker=null;var activeAzureFinish=null;var activeBrowserFinish=null;var activeElevenLabsAudio=null;var activeElevenLabsAudioUrl=null;var activeElevenLabsAbortController=null;var activeElevenLabsFinish=null;var cachedPickaxeId=null;var readoutConfigPromise=null;var recognition=null;var recognitionInput=null;var recognitionBaseText="";var acceptRecognitionResults=false;var composerClearTimer=null;var ttsRunId=0;var ttsSessionActive=false;var keepListening=false;var recognitionIsActive=false;var agentIsResponding=false;var agentIsSpeaking=false;var voiceInputUsedSinceLastSubmission=false;var resumeMicAfterAgentResponse=false;var attachedMicButton=null;var attachedMicButtonOriginalStyle=null;function escapeSsml(value){return String(value||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function isVisible(el){if(!el)return false;var rect=el.getBoundingClientRect();var style=window.getComputedStyle(el);return rect.width>0&&rect.height>0&&style.display!=="none"&&style.visibility!=="hidden"}function isPossiblePickaxeId(value){return typeof value==="string"&&/^[A-Za-z0-9_-]{8,80}$/.test(value)&&!/^portal(?:_|-)?\d*$/i.test(value)&&!/^deployment-/i.test(value)&&!/^studio/i.test(value)}function findPickaxeIdInObject(value,depth,seen){if(!value||typeof value!=="object"||depth>7)return null;if(seen.has(value))return null;seen.add(value);var directKeys=["formid","formId","pickaxeId","pickaxe_id"];for(var i=0;i<directKeys.length;i++){var directValue=value[directKeys[i]];if(isPossiblePickaxeId(directValue))return directValue}var keys=Object.keys(value);for(var j=0;j<keys.length;j++){var nestedValue=value[keys[j]];var nestedId=findPickaxeIdInObject(nestedValue,depth+1,seen);if(nestedId)return nestedId}return null}function getStructuredNextData(){var nextDataElement=document.getElementById("__NEXT_DATA__");if(!nextDataElement||!nextDataElement.textContent)return null;try{return JSON.parse(nextDataElement.textContent)}catch(error){return null}}function getPickaxeId(){if(isPossiblePickaxeId(PICKAXE_ID))return PICKAXE_ID;if(cachedPickaxeId)return cachedPickaxeId;var params=new URLSearchParams(window.location.search);var parameterNames=["pickaxeId","pickaxe_id","formId","formid"];for(var i=0;i<parameterNames.length;i++){var parameterValue=params.get(parameterNames[i]);if(isPossiblePickaxeId(parameterValue)){cachedPickaxeId=parameterValue;return cachedPickaxeId}}var pathParts=window.location.pathname.split("/").map(function(part){try{return decodeURIComponent(part)}catch(error){return part}}).filter(Boolean);var currentPathPart=pathParts[pathParts.length-1]||"";var structuredNextData=getStructuredNextData();if(/^deployment-/i.test(currentPathPart)&&structuredNextData){var deployments=structuredNextData.props&&structuredNextData.props.pageProps&&structuredNextData.props.pageProps.preloadedStudio&&structuredNextData.props.pageProps.preloadedStudio.deployments;if(Array.isArray(deployments)){var activeDeployment=deployments.find(function(deployment){return deployment&&(deployment.deploymentId===currentPathPart||deployment.path===currentPathPart)});var deploymentFormId=activeDeployment&&(activeDeployment.formId||activeDeployment.formid);if(isPossiblePickaxeId(deploymentFormId)){cachedPickaxeId=deploymentFormId;return cachedPickaxeId}}}for(var pathIndex=pathParts.length-1;pathIndex>=0;pathIndex--){if(isPossiblePickaxeId(pathParts[pathIndex])){cachedPickaxeId=pathParts[pathIndex];return cachedPickaxeId}}var idElement=document.querySelector("[data-pickaxe-id], [data-pickaxeid], [data-form-id], [data-formid]");if(idElement){var attributeNames=["data-pickaxe-id","data-pickaxeid","data-form-id","data-formid"];for(var j=0;j<attributeNames.length;j++){var attributeValue=idElement.getAttribute(attributeNames[j]);if(isPossiblePickaxeId(attributeValue)){cachedPickaxeId=attributeValue;return cachedPickaxeId}}}try{var nextDataId=findPickaxeIdInObject(structuredNextData||window.__NEXT_DATA__,0,new WeakSet);if(nextDataId){cachedPickaxeId=nextDataId;return cachedPickaxeId}}catch(error){}return null}function getAzureLocaleFromVoiceName(voiceName){var match=String(voiceName||"").match(/^([a-z]{2,3}-[A-Z]{2})-/);return match?match[1]:"en-US"}function loadAgentReadoutConfiguration(){if(readoutConfigPromise)return readoutConfigPromise;readoutConfigPromise=async function(){var pickaxeId=getPickaxeId();if(!pickaxeId){throw new Error("Could not determine the Pickaxe ID for voice settings.")}var response=await fetch(PICKAXE_API2_BASE+"/?pickaxeId="+encodeURIComponent(pickaxeId),{method:"GET",credentials:window.location.hostname.indexOf("pickaxe.co")!==-1?"include":"omit"});if(!response.ok){throw new Error("Could not load agent voice settings: "+response.status)}var payload=await response.json();var settings=payload&&payload.data;if(!settings){throw new Error("Agent voice settings were not returned.")}PICKAXE_READOUT_VOICE=settings.readoutvoice||null;if(settings.readoutprovider==="azure"||settings.readoutprovider==="elevenlabs"){PICKAXE_READOUT_PROVIDER=settings.readoutprovider}else{PICKAXE_READOUT_PROVIDER=/^[a-z]{2,3}-[A-Z]{2}-/.test(PICKAXE_READOUT_VOICE||"")?"azure":PICKAXE_READOUT_VOICE?"elevenlabs":"azure"}if(PICKAXE_READOUT_PROVIDER==="azure"){PICKAXE_READOUT_LOCALE=getAzureLocaleFromVoiceName(PICKAXE_READOUT_VOICE)}console.info("Pickaxe voice configuration loaded.",{pickaxeId:pickaxeId,provider:PICKAXE_READOUT_PROVIDER,voice:PICKAXE_READOUT_VOICE});return settings}().catch(function(error){readoutConfigPromise=null;throw error});return readoutConfigPromise}function ensureListeningAnimationStyle(){if(document.getElementById("pickaxe-voice-loop-listening-style-v2"))return;var style=document.createElement("style");style.id="pickaxe-voice-loop-listening-style-v2";style.textContent="@keyframes pickaxeVoiceLoopListeningPulse {"+"0% { box-shadow: 0 0 0 0 rgba(0, 100, 255, 0.45), 0 8px 20px rgba(0, 100, 255, 0.24); transform: scale(1); }"+"70% { box-shadow: 0 0 0 10px rgba(0, 100, 255, 0), 0 8px 20px rgba(0, 100, 255, 0.24); transform: scale(1.03); }"+"100% { box-shadow: 0 0 0 0 rgba(0, 100, 255, 0), 0 8px 20px rgba(0, 100, 255, 0.24); transform: scale(1); }"+"}"+"[data-continuous-voice-input='on'] {"+"animation: pickaxeVoiceLoopListeningPulse 1.25s ease-out infinite !important;"+"}";document.head.appendChild(style)}function loadSpeechSdk(){if(window.SpeechSDK)return Promise.resolve(window.SpeechSDK);if(sdkPromise)return sdkPromise;sdkPromise=new Promise(function(resolve,reject){var script=document.createElement("script");script.src="https://cdn.jsdelivr.net/npm/microsoft-cognitiveservices-speech-sdk/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.js";script.async=true;script.onload=function(){if(window.SpeechSDK)resolve(window.SpeechSDK);else reject(new Error("Azure Speech SDK did not load."))};script.onerror=function(){reject(new Error("Could not load Azure Speech SDK."))};document.head.appendChild(script)});return sdkPromise}async function getSpeechToken(){var response=await fetch(PICKAXE_API2_BASE+"/speech",{method:"POST",headers:{"Content-Type":"application/json"},credentials:window.location.hostname.indexOf("pickaxe.co")!==-1?"include":"omit",body:"{}"});if(!response.ok){throw new Error("Speech token request failed: "+response.status)}var data=await response.json();if(!data||!data.success||!data.token){throw new Error(data&&data.error||"Speech token was not returned.")}return data.token}function stopCurrentAudio(){if(activeAzureFinish){var finishAzure=activeAzureFinish;activeAzureFinish=null;finishAzure()}if(activeBrowserFinish){var finishBrowser=activeBrowserFinish;activeBrowserFinish=null;finishBrowser()}try{if(activeElevenLabsAbortController){activeElevenLabsAbortController.abort()}}catch(error){}if(activeElevenLabsFinish){var finishElevenLabs=activeElevenLabsFinish;activeElevenLabsFinish=null;finishElevenLabs()}try{if(activeElevenLabsAudio){activeElevenLabsAudio.pause();activeElevenLabsAudio.removeAttribute("src");activeElevenLabsAudio.load()}}catch(error){}if(activeElevenLabsAudioUrl){URL.revokeObjectURL(activeElevenLabsAudioUrl)}activeElevenLabsAbortController=null;activeElevenLabsAudio=null;activeElevenLabsAudioUrl=null;try{window.speechSynthesis.cancel()}catch(error){}try{if(activeSynthesizer)activeSynthesizer.close()}catch(error){}try{if(activeSpeaker)activeSpeaker.pause()}catch(error){}try{if(activeSpeaker)activeSpeaker.close()}catch(error){}activeSynthesizer=null;activeSpeaker=null}async function speakWithElevenLabs(text){var pickaxeId=getPickaxeId();if(!pickaxeId){throw new Error("Could not determine the Pickaxe ID required for ElevenLabs speech.")}stopCurrentAudio();activeElevenLabsAbortController=new AbortController;var response=await fetch(PICKAXE_API2_BASE+"/speech/elevenlabs/stream",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:text,pickaxeId:pickaxeId}),signal:activeElevenLabsAbortController.signal});if(!response.ok){var errorMessage="ElevenLabs speech synthesis failed: "+response.status;try{var errorBody=await response.json();errorMessage=errorBody.message||errorBody.detail&&errorBody.detail.message||errorMessage}catch(error){}throw new Error(errorMessage)}return new Promise(function(resolve,reject){var audio=new Audio;var mediaSource=null;var settled=false;activeElevenLabsAudio=audio;function finish(error){if(settled)return;settled=true;audio.onended=null;audio.onerror=null;if(activeElevenLabsAudio===audio){activeElevenLabsFinish=null;activeElevenLabsAbortController=null;activeElevenLabsAudio=null;if(activeElevenLabsAudioUrl){URL.revokeObjectURL(activeElevenLabsAudioUrl);activeElevenLabsAudioUrl=null}}if(error)reject(error);else resolve()}activeElevenLabsFinish=function(){finish()};audio.onended=function(){finish()};audio.onerror=function(){finish(new Error("ElevenLabs audio playback failed."))};(async function(){if(!("MediaSource"in window)||!MediaSource.isTypeSupported("audio/mpeg")||!response.body){var audioBlob=await response.blob();if(!audioBlob.size){throw new Error("ElevenLabs returned no audio.")}activeElevenLabsAudioUrl=URL.createObjectURL(audioBlob);audio.src=activeElevenLabsAudioUrl;await audio.play();return}mediaSource=new MediaSource;activeElevenLabsAudioUrl=URL.createObjectURL(mediaSource);audio.src=activeElevenLabsAudioUrl;var sourceBuffer=await new Promise(function(resolveSource,rejectSource){mediaSource.addEventListener("sourceopen",function(){try{resolveSource(mediaSource.addSourceBuffer("audio/mpeg"))}catch(error){rejectSource(error)}},{once:true})});var reader=response.body.getReader();var playbackStarted=false;while(!settled){var chunk=await reader.read();if(chunk.done)break;if(!chunk.value||!chunk.value.byteLength)continue;await new Promise(function(resolveAppend,rejectAppend){function cleanup(){sourceBuffer.removeEventListener("updateend",handleUpdateEnd);sourceBuffer.removeEventListener("error",handleBufferError)}function handleUpdateEnd(){cleanup();resolveAppend()}function handleBufferError(){cleanup();rejectAppend(new Error("ElevenLabs audio buffer failed."))}sourceBuffer.addEventListener("updateend",handleUpdateEnd);sourceBuffer.addEventListener("error",handleBufferError);sourceBuffer.appendBuffer(chunk.value)});if(!playbackStarted){playbackStarted=true;audio.play().catch(function(error){finish(error)})}}if(!playbackStarted&&!settled){throw new Error("ElevenLabs returned no audio.")}if(!settled&&mediaSource.readyState==="open"){mediaSource.endOfStream()}})().catch(function(error){finish(error)})})}async function speakWithAzure(text,useSavedVoice){var SpeechSDK=await loadSpeechSdk();var token=await getSpeechToken();stopCurrentAudio();var speechConfig=SpeechSDK.SpeechConfig.fromAuthorizationToken(token,AZURE_SPEECH_REGION);speechConfig.speechSynthesisLanguage=PICKAXE_READOUT_LOCALE;speechConfig.speechSynthesisOutputFormat=SpeechSDK.SpeechSynthesisOutputFormat.Audio24Khz48KBitRateMonoMp3;activeSpeaker=new SpeechSDK.SpeakerAudioDestination;var audioConfig=SpeechSDK.AudioConfig.fromSpeakerOutput(activeSpeaker);activeSynthesizer=new SpeechSDK.SpeechSynthesizer(speechConfig,audioConfig);var azureVoiceName=useSavedVoice===false?null:PICKAXE_READOUT_VOICE;if(!azureVoiceName){var voicesResult=await activeSynthesizer.getVoicesAsync(PICKAXE_READOUT_LOCALE);azureVoiceName=voicesResult.voices.find(function(voice){return voice.locale===PICKAXE_READOUT_LOCALE})?.shortName}if(!azureVoiceName){throw new Error("No Azure voice is available for "+PICKAXE_READOUT_LOCALE)}var ssml='<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="'+escapeSsml(PICKAXE_READOUT_LOCALE)+'">'+'<voice name="'+escapeSsml(azureVoiceName)+'">'+escapeSsml(text)+"</voice></speak>";return new Promise(function(resolve,reject){var settled=false;var synthesisTimer=null;var playbackMonitor=null;var synthesizer=activeSynthesizer;var speaker=activeSpeaker;var internalAudio=null;function releaseSynthesizer(){if(activeSynthesizer===synthesizer){activeSynthesizer=null}try{synthesizer.close()}catch(error){}}function releaseSpeaker(){clearInterval(playbackMonitor);if(internalAudio){internalAudio.removeEventListener("ended",handlePlaybackEnded);internalAudio.removeEventListener("pause",checkPlaybackState)}if(activeSpeaker===speaker)activeSpeaker=null;try{speaker.pause()}catch(error){}try{speaker.close()}catch(error){}}function handlePlaybackEnded(){finish()}function bindInternalAudio(){var nextAudio=speaker.internalAudio;if(!nextAudio||nextAudio===internalAudio)return;if(internalAudio){internalAudio.removeEventListener("ended",handlePlaybackEnded);internalAudio.removeEventListener("pause",checkPlaybackState)}internalAudio=nextAudio;internalAudio.addEventListener("ended",handlePlaybackEnded,{once:true});internalAudio.addEventListener("pause",checkPlaybackState)}function checkPlaybackState(){if(settled)return;bindInternalAudio();if(!internalAudio)return;var duration=internalAudio.duration;var reachedEnd=internalAudio.ended||internalAudio.paused&&internalAudio.currentTime>0&&Number.isFinite(duration)&&duration-internalAudio.currentTime<=.25;if(reachedEnd)finish()}function finish(error){if(settled)return;settled=true;clearTimeout(synthesisTimer);if(activeAzureFinish===finish)activeAzureFinish=null;releaseSynthesizer();releaseSpeaker();if(error)reject(error);else resolve()}activeAzureFinish=finish;speaker.onAudioStart=function(){clearTimeout(synthesisTimer);bindInternalAudio();clearInterval(playbackMonitor);playbackMonitor=setInterval(checkPlaybackState,100)};speaker.onAudioEnd=function(){finish()};synthesisTimer=setTimeout(function(){finish(new Error("Azure speech synthesis timed out."))},3e4);synthesizer.speakSsmlAsync(ssml,function(result){clearTimeout(synthesisTimer);if(settled)return;if(result.reason!==SpeechSDK.ResultReason.SynthesizingAudioCompleted){finish(new Error(result.errorDetails||"Azure speech synthesis failed."));return}releaseSynthesizer();bindInternalAudio();checkPlaybackState()},function(error){finish(error)})})}function speakWithBrowserFallback(text){stopCurrentAudio();return new Promise(function(resolve){var utterance=new SpeechSynthesisUtterance(text);var settled=false;var voices=window.speechSynthesis.getVoices();function finish(){if(settled)return;settled=true;if(activeBrowserFinish===finish)activeBrowserFinish=null;resolve()}activeBrowserFinish=finish;var localVoice=voices.find(function(voice){return voice.name===PICKAXE_READOUT_VOICE})||voices.find(function(voice){return voice.lang===PICKAXE_READOUT_LOCALE})||voices.find(function(voice){return voice.lang&&voice.lang.indexOf("en")===0});utterance.lang=PICKAXE_READOUT_LOCALE;if(localVoice)utterance.voice=localVoice;utterance.rate=1;utterance.pitch=1;utterance.volume=1;utterance.onend=finish;utterance.onerror=finish;window.speechSynthesis.speak(utterance)})}async function speakWithConfiguredProvider(text){await loadAgentReadoutConfiguration();var provider=String(PICKAXE_READOUT_PROVIDER||"azure").toLowerCase();if(provider==="azure"){return speakWithAzure(text,true)}try{return await speakWithElevenLabs(text)}catch(error){console.warn("ElevenLabs text-to-speech was unavailable; using Azure speech.",error);return speakWithAzure(text,false)}}async function speakAssistantText(text){if(!text||text===lastSpokenText)return;lastSpokenText=text;var currentTtsRunId=++ttsRunId;agentIsResponding=false;agentIsSpeaking=true;ttsSessionActive=true;stopListening();try{await speakWithConfiguredProvider(text)}catch(error){console.warn("Configured Pickaxe text-to-speech failed; using a browser voice.",error);await speakWithBrowserFallback(text)}finally{if(currentTtsRunId!==ttsRunId)return;ttsSessionActive=false;agentIsSpeaking=false;updateExistingMicButton();var shouldResumeMic=AUTO_TURN_MIC_ON_AFTER_AGENT_RESPONSE&&resumeMicAfterAgentResponse;resumeMicAfterAgentResponse=false;if(shouldResumeMic){startListening()}}}function getLastAssistantText(){var selectors=[".test-chat-message",".px-builder-preview-message-parts",'[data-role="assistant"]','[data-message-role="assistant"]',".assistant-message",'[class*="AssistantMessage"]','[class*="assistant-message"]','[class*="bot-message"]','[class*="ai-message"]'];for(var i=0;i<selectors.length;i++){var els=document.querySelectorAll(selectors[i]);if(els.length>0){return els[els.length-1].innerText.trim()}}var candidates=document.querySelectorAll(['[class*="message"]','[class*="Message"]','[class*="bubble"]','[class*="Bubble"]','[class*="response"]','[class*="Response"]'].join(", "));if(candidates.length>0){return candidates[candidates.length-1].innerText.trim()}return null}function isUsableAssistantText(text){var normalized=String(text||"").trim().toLowerCase();if(!normalized)return false;return!["...","thinking","thinking...","loading","loading...","generating","generating...","please wait","please wait..."].includes(normalized)}function isAgentResponseInProgress(){if(!agentIsResponding)return false;var knownComposer=document.getElementById("preview-chat-input");if(knownComposer){return!document.getElementById("chat-submit-btn")}var busyElement=Array.from(document.querySelectorAll("[aria-busy='true'], [data-loading='true'], [data-state='loading']")).find(isVisible);if(busyElement)return true;var stopButton=Array.from(document.querySelectorAll("button")).filter(isVisible).find(function(button){var label=[button.innerText,button.textContent,button.getAttribute("aria-label"),button.getAttribute("title")].join(" ").toLowerCase();return label.includes("stop generating")||label.includes("stop response")});return Boolean(stopButton)}function findComposer(){var selectors=["textarea:not([disabled])","[contenteditable='true']","[role='textbox']","input[type='text']:not([disabled])"];var candidates=Array.from(document.querySelectorAll(selectors.join(","))).filter(isVisible);return candidates[candidates.length-1]||null}function setNativeValue(el,value){var prototype=Object.getPrototypeOf(el);var descriptor=Object.getOwnPropertyDescriptor(prototype,"value");if(descriptor&&descriptor.set){descriptor.set.call(el,value)}else{el.value=value}}function getComposerText(input){if(!input)return"";if(input.tagName==="TEXTAREA"||input.tagName==="INPUT"){return input.value||""}return input.innerText||input.textContent||""}function renderTranscriptPreview(finalText,interimText){if(!acceptRecognitionResults)return;var input=findComposer();if(!input)return;if(input!==recognitionInput){recognitionInput=input;recognitionBaseText=getComposerText(input)}var transcript=[finalText,interimText].filter(Boolean).join(" ").replace(/\s+/g," ").trim();if(!transcript)return;input.focus();var separator=recognitionBaseText.trim()?" ":"";var nextValue=recognitionBaseText+separator+transcript;if(input.tagName==="TEXTAREA"||input.tagName==="INPUT"){setNativeValue(input,nextValue);input.dispatchEvent(new InputEvent("input",{bubbles:true,inputType:"insertText",data:transcript}));input.dispatchEvent(new Event("change",{bubbles:true}))}else{input.textContent=nextValue;input.dispatchEvent(new InputEvent("input",{bubbles:true,inputType:"insertText",data:transcript}))}}function clearComposerValue(input){if(!input)return;if(input.tagName==="TEXTAREA"||input.tagName==="INPUT"){setNativeValue(input,"");input.dispatchEvent(new InputEvent("input",{bubbles:true,inputType:"deleteContentBackward",data:null}));input.dispatchEvent(new Event("change",{bubbles:true}))}else{input.textContent="";input.dispatchEvent(new InputEvent("input",{bubbles:true,inputType:"deleteContentBackward",data:null}))}}function clearComposerAfterConfirmedSubmission(submittedText){clearTimeout(composerClearTimer);if(!submittedText)return;var attempts=0;function clearWhenResponseStarts(){if(!agentIsResponding)return;var input=findComposer();if(!input)return;if(getComposerText(input)!==submittedText)return;if(isAgentResponseInProgress()){clearComposerValue(input);return}attempts+=1;if(attempts<30){composerClearTimer=setTimeout(clearWhenResponseStarts,50)}}composerClearTimer=setTimeout(clearWhenResponseStarts,50)}function findSendButton(){var knownSendButton=document.getElementById("chat-submit-btn");if(knownSendButton&&isVisible(knownSendButton)){return knownSendButton}var buttons=Array.from(document.querySelectorAll("button")).filter(isVisible).filter(function(button){var text=(button.innerText||button.textContent||"").toLowerCase();var aria=(button.getAttribute("aria-label")||"").toLowerCase();var title=(button.getAttribute("title")||"").toLowerCase();return text.includes("send")||text.includes("submit")||aria.includes("send")||aria.includes("submit")||title.includes("send")||title.includes("submit")||button.type==="submit"});return buttons[buttons.length-1]||null}function markAgentResponsePending(){if(agentIsResponding)return;var composer=findComposer();var submittedText=getComposerText(composer);resumeMicAfterAgentResponse=AUTO_TURN_MIC_ON_AFTER_AGENT_RESPONSE&&keepListening&&voiceInputUsedSinceLastSubmission;voiceInputUsedSinceLastSubmission=false;lastSpokenText="";cancelActiveTts();acceptRecognitionResults=false;recognitionInput=null;recognitionBaseText="";agentIsResponding=true;stopListening();updateExistingMicButton();clearComposerAfterConfirmedSubmission(submittedText)}function handleSendButtonClick(event){var target=event.target;if(!(target instanceof Element))return;var clickedControl=target.closest("button, [role='button']");if(clickedControl&&clickedControl.id==="chat-submit-btn"){if(clickedControl.disabled)return;markAgentResponsePending();return}var sendButton=findSendButton();if(clickedControl&&sendButton&&clickedControl===sendButton){if(sendButton.disabled)return;markAgentResponsePending()}}function handleComposerKeydown(event){if(event.key!=="Enter"||event.shiftKey||event.isComposing||event.defaultPrevented){return}var composer=findComposer();if(!composer)return;if(!getComposerText(composer).trim())return;var sendButton=findSendButton();if(sendButton&&sendButton.disabled)return;var target=event.target;if(target===composer||target instanceof Node&&composer.contains(target)){markAgentResponsePending()}}function handleComposerSubmit(event){var composer=findComposer();var form=event.target;if(composer&&form instanceof HTMLFormElement&&form.contains(composer)){markAgentResponsePending()}}function updateExistingMicButton(){if(!attachedMicButton)return;ensureListeningAnimationStyle();var showListeningState=recognitionIsActive&&!agentIsResponding&&!agentIsSpeaking;attachedMicButton.setAttribute("aria-pressed",showListeningState?"true":"false");attachedMicButton.setAttribute("data-continuous-voice-input",showListeningState?"on":"off");if(showListeningState){attachedMicButton.style.display="";attachedMicButton.style.visibility="visible";attachedMicButton.style.opacity="1";attachedMicButton.style.pointerEvents="auto";attachedMicButton.style.outline="2px solid #0064ff";attachedMicButton.style.outlineOffset="3px";attachedMicButton.style.backgroundColor="#0064ff";attachedMicButton.style.color="#ffffff";attachedMicButton.style.borderRadius="999px";attachedMicButton.title="Microphone is on";attachedMicButton.setAttribute("aria-label","Microphone is on")}else{if(attachedMicButtonOriginalStyle!==null){attachedMicButton.setAttribute("style",attachedMicButtonOriginalStyle)}else{attachedMicButton.style.outline="";attachedMicButton.style.outlineOffset="";attachedMicButton.style.backgroundColor="";attachedMicButton.style.color="";attachedMicButton.style.borderRadius="";attachedMicButton.style.boxShadow=""}var idleLabel=agentIsSpeaking||ttsSessionActive?"Stop speaking and turn microphone on":"Microphone is off";attachedMicButton.title=idleLabel;attachedMicButton.setAttribute("aria-label",idleLabel)}}function createRecognition(){if(!SpeechRecognition)return;recognition=new SpeechRecognition;recognition.lang=VOICE_INPUT_LANG;recognition.continuous=true;recognition.interimResults=true;recognition.maxAlternatives=1;recognition.onstart=function(){recognitionInput=findComposer();recognitionBaseText=getComposerText(recognitionInput);acceptRecognitionResults=true;recognitionIsActive=true;updateExistingMicButton()};recognition.onresult=function(event){var finalText="";var interimText="";for(var i=0;i<event.results.length;i++){if(event.results[i].isFinal){finalText+=" "+event.results[i][0].transcript}else{interimText+=" "+event.results[i][0].transcript}}finalText=finalText.trim();interimText=interimText.trim();if(finalText||interimText){voiceInputUsedSinceLastSubmission=true}renderTranscriptPreview(finalText,interimText)};recognition.onerror=function(event){acceptRecognitionResults=false;recognitionIsActive=false;updateExistingMicButton();if(event.error==="not-allowed"||event.error==="service-not-allowed"){keepListening=false;updateExistingMicButton()}};recognition.onend=function(){acceptRecognitionResults=false;recognitionIsActive=false;updateExistingMicButton();if(!keepListening||agentIsResponding||agentIsSpeaking)return;clearTimeout(restartTimer);restartTimer=setTimeout(function(){try{recognition.start()}catch(error){}},RESTART_LISTENING_DELAY_MS)}}function startListening(){if(!SpeechRecognition){console.warn("Speech recognition is not supported in this browser.");return}if(agentIsResponding||ttsSessionActive)return;attachToExistingMicButton();keepListening=true;if(!recognition){createRecognition()}try{recognition.start()}catch(error){}}function stopListening(){keepListening=false;recognitionIsActive=false;updateExistingMicButton();clearTimeout(restartTimer);try{if(recognition)recognition.stop()}catch(error){}}function cancelActiveTts(){if(!ttsSessionActive&&!agentIsSpeaking)return false;ttsRunId+=1;ttsSessionActive=false;agentIsSpeaking=false;stopCurrentAudio();updateExistingMicButton();return true}function toggleListening(event){event.preventDefault();event.stopPropagation();event.stopImmediatePropagation();if(cancelActiveTts()){agentIsResponding=false;startListening();return}if(keepListening){stopListening()}else{if(!ttsSessionActive){agentIsResponding=false;agentIsSpeaking=false}startListening()}}function handleDelegatedMicButtonClick(event){var target=event.target;if(!(target instanceof Element))return;var button=target.closest("button");if(!button||!looksLikeMicButton(button))return;if(button!==attachedMicButton){attachedMicButton=button;attachedMicButtonOriginalStyle=button.getAttribute("style")||""}attachedMicButton.setAttribute("data-continuous-voice-input-attached","true");toggleListening(event)}function looksLikeMicButton(button){var text=(button.innerText||button.textContent||"").toLowerCase();var aria=(button.getAttribute("aria-label")||"").toLowerCase();var title=(button.getAttribute("title")||"").toLowerCase();var testId=(button.getAttribute("data-testid")||"").toLowerCase();var html=button.innerHTML.toLowerCase();var svgPathData=Array.from(button.querySelectorAll("svg path")).map(function(path){return(path.getAttribute("d")||"").toLowerCase().replace(/\s+/g," ")});var hasPickaxeMicrophonePath=svgPathData.some(function(pathData){return pathData.includes("m12 1a3 3 0 0 0-3 3v8")||pathData.includes("m19 10v2a7 7 0 0 1-14 0v-2")});return hasPickaxeMicrophonePath||text.includes("mic")||text.includes("microphone")||text.includes("voice")||aria.includes("mic")||aria.includes("microphone")||aria.includes("voice")||title.includes("mic")||title.includes("microphone")||title.includes("voice")||testId.includes("mic")||testId.includes("microphone")||testId.includes("voice")||html.includes("microphone")||html.includes("mic_svg")||html.includes("voice")}function attachToExistingMicButton(){if(!SpeechRecognition)return;var buttons=Array.from(document.querySelectorAll("button")).filter(isVisible).filter(looksLikeMicButton);var button=buttons[buttons.length-1];if(!button||button===attachedMicButton)return;attachedMicButton=button;attachedMicButtonOriginalStyle=button.getAttribute("style")||"";attachedMicButton.setAttribute("data-continuous-voice-input-attached","true");updateExistingMicButton()}var observer=new MutationObserver(function(){if(!attachedMicButton||!attachedMicButton.isConnected){attachToExistingMicButton()}clearTimeout(ttsDebounceTimer);ttsDebounceTimer=setTimeout(function(){if(!agentIsResponding)return;if(isAgentResponseInProgress())return;var text=getLastAssistantText();if(isUsableAssistantText(text))speakAssistantText(text)},TTS_DEBOUNCE_MS)});function init(){ensureListeningAnimationStyle();attachToExistingMicButton();document.addEventListener("click",handleDelegatedMicButtonClick,true);document.addEventListener("click",handleSendButtonClick,true);document.addEventListener("keydown",handleComposerKeydown,true);document.addEventListener("submit",handleComposerSubmit,true);observer.observe(document.body,{childList:true,subtree:true,characterData:true})}if(document.body){init()}else{document.addEventListener("DOMContentLoaded",init)}})();
</script>
Please go ahead and test this out, make some improvements, and let me know what you guys think!
