From de2504ce0392ba003a2fe705a2d237809750d6d2 Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Tue, 17 Dec 2024 09:51:15 -0800 Subject: [PATCH] Update the speech input/output --- src/quartapp/static/speech-input.js | 43 +++++++++++++++++++++++----- src/quartapp/static/speech-output.js | 23 +++++++++++++-- src/quartapp/templates/index.html | 2 +- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/quartapp/static/speech-input.js b/src/quartapp/static/speech-input.js index 4994c38..40a780f 100644 --- a/src/quartapp/static/speech-input.js +++ b/src/quartapp/static/speech-input.js @@ -15,16 +15,31 @@ class SpeechInputButton extends HTMLElement { this.speechRecognition = new SpeechRecognition(); this.speechRecognition.lang = navigator.language || navigator.userLanguage; this.speechRecognition.interimResults = false; + this.speechRecognition.continuous = true; this.speechRecognition.maxAlternatives = 1; } connectedCallback() { this.innerHTML = ` - `; this.recordButton = this.querySelector("button"); this.recordButton.addEventListener("click", () => this.toggleRecording()); + document.addEventListener('keydown', this.handleKeydown.bind(this)); + } + + disconnectedCallback() { + document.removeEventListener('keydown', this.handleKeydown.bind(this)); + } + + handleKeydown(event) { + if (event.key === 'Escape') { + this.abortRecording(); + } else if (event.key === ' ' && event.shiftKey) { // Shift + Space + event.preventDefault(); // Prevent default action + this.toggleRecording(); + } } renderButtonOn() { @@ -37,6 +52,15 @@ class SpeechInputButton extends HTMLElement { this.recordButton.innerHTML = ''; } + + toggleRecording() { + if (this.isRecording) { + this.stopRecording(); + } else { + this.startRecording(); + } + } + startRecording() { if (this.speechRecognition == null) { this.dispatchEvent( @@ -86,11 +110,11 @@ class SpeechInputButton extends HTMLElement { }, }) ); - } else { + } else if (event.error != "aborted") { this.dispatchEvent( new CustomEvent("speech-input-error", { detail: { - error: "An error occurred while recording. Please try again.", + error: "An error occurred while recording. Please try again: " + event.error, }, }) ); @@ -103,13 +127,18 @@ class SpeechInputButton extends HTMLElement { this.renderButtonOn(); } - toggleRecording() { - if (this.isRecording) { + stopRecording() { + if (this.speechRecognition) { this.speechRecognition.stop(); - } else { - this.startRecording(); } } + + abortRecording() { + if (this.speechRecognition) { + this.speechRecognition.abort(); + } + } + } customElements.define("speech-input-button", SpeechInputButton); diff --git a/src/quartapp/static/speech-output.js b/src/quartapp/static/speech-output.js index 0b897db..dc890f2 100644 --- a/src/quartapp/static/speech-output.js +++ b/src/quartapp/static/speech-output.js @@ -27,6 +27,17 @@ class SpeechOutputButton extends HTMLElement { this.speechButton.addEventListener("click", () => this.toggleSpeechOutput() ); + document.addEventListener('keydown', this.handleKeydown.bind(this)); + } + + disconnectedCallback() { + document.removeEventListener('keydown', this.handleKeydown.bind(this)); + } + + handleKeydown(event) { + if (event.key === 'Escape') { + this.stopSpeech(); + } } renderButtonOn() { @@ -46,9 +57,7 @@ class SpeechOutputButton extends HTMLElement { const text = this.getAttribute("text"); if (this.synth != null) { if (this.isPlaying || text === "") { - this.synth.cancel(); // removes all utterances from the utterance queue. - this.isPlaying = false; - this.renderButtonOff(); + this.stopSpeech(); return; } @@ -86,6 +95,14 @@ class SpeechOutputButton extends HTMLElement { }; } } + + stopSpeech() { + if (this.synth) { + this.synth.cancel(); + this.isPlaying = false; + this.renderButtonOff(); + } + } } customElements.define("speech-output-button", SpeechOutputButton); diff --git a/src/quartapp/templates/index.html b/src/quartapp/templates/index.html index 13b620c..20a562a 100644 --- a/src/quartapp/templates/index.html +++ b/src/quartapp/templates/index.html @@ -189,7 +189,7 @@

Chat with your uploaded images< const speechOutput = document.createElement("speech-output-button"); speechOutput.setAttribute("text", answer); messageDiv.appendChild(speechOutput); - messageDiv.focus(); + messageInput.focus(); } catch (error) { messageDiv.innerHTML = "Error: " + error; }