Skip to content

Commit

Permalink
Add voice assistant for Chrome and Safari (#22)
Browse files Browse the repository at this point in the history
* Add voice-to-speech feature for Chrome
* Support shift+enter
* Fix speech recognition on MacOS Safari
* Update screenshot with a microphone button
  • Loading branch information
arey authored Dec 31, 2024
1 parent e63d48c commit 62e5f5e
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 9 deletions.
Binary file modified docs/chat-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 63 additions & 2 deletions src/main/resources/static/resources/css/chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
position: fixed;
bottom: 10px;
right: 10px;
width: 300px;
width: 400px;
background-color: #f1f1f1;
border-radius: 10px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
Expand Down Expand Up @@ -52,7 +52,7 @@

/* Chat bubbles styling */
.chat-bubble {
max-width: 80%;
max-width: 85%;
padding: 10px;
border-radius: 20px;
margin-bottom: 10px;
Expand Down Expand Up @@ -113,3 +113,64 @@
.chatbox-footer button:hover {
background-color: #128C7E;
}

#mic-button {
margin-left: 0;
border-radius: 50%;
width: 40px;
height: 40px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}

#mic-button.active {
background-color: #dc3545;
border-color: #dc3545;
color: white;
}

#mic-button.active:hover {
background-color: #bb2d3b;
border-color: #b02a37;
}

#mic-button i {
font-size: 1.2em;
}

/* Ajout du style pour le bouton d'envoi */
#chatbox-input-container {
padding: 10px;
background-color: #f9f9f9;
display: flex;
align-items: center;
gap: 5px;
}

#chatbox-input {
flex: 1;
padding: 8px;
border-radius: 20px;
border: 1px solid #ccc;
resize: none;
outline: none;
margin: 0;
}

#chatbox-input-container button {
flex-shrink: 0; /* Empêche les boutons de rétrécir */
border-radius: 50%;
width: 40px;
height: 40px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
margin: 0;
}

#chatbox-input-container button i {
font-size: 1.2em;
}
106 changes: 102 additions & 4 deletions src/main/resources/static/resources/js/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,11 @@ async function displayBotReply(response) {
}
}

function handleKeyPress(event) {
if (event.key === "Enter") {
event.preventDefault(); // Prevents adding a newline
sendMessage(); // Send the message when Enter is pressed
function handleKeyDown(event) {
// If it's the Enter key without the Shift key
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault(); // Prevents line feed
sendMessage();
}
}

Expand All @@ -134,3 +135,100 @@ function uuidv4() {
return v.toString(16);
});
}

let recognition = null;
let isListening = false;

function initializeSpeechRecognition() {
if ('webkitSpeechRecognition' in window) {
recognition = new webkitSpeechRecognition();
recognition.continuous = false;
recognition.interimResults = true;

const browserLang = navigator.language || navigator.userLanguage;
recognition.lang = browserLang || 'en-US';

let finalTranscript = '';

recognition.onresult = function(event) {
const input = document.getElementById('chatbox-input');
const lastResult = event.results[event.results.length - 1];

if (lastResult.isFinal) {
finalTranscript = lastResult[0].transcript;
input.value = finalTranscript;
recognition.stop();
} else {
input.value = finalTranscript + lastResult[0].transcript;
}
};

recognition.onend = function() {
const input = document.getElementById('chatbox-input');
if (input.value.trim()) {
setTimeout(() => {
sendMessage();
}, 100);
}
toggleMicrophoneButton(false);
isListening = false;
finalTranscript = '';
};

recognition.onerror = function(event) {
const userElements = prepareMessage('bot');
let errorMessage = 'Speech recognition error: ';

switch(event.error) {
case 'network':
errorMessage += 'Network error occurred';
break;
case 'no-speech':
errorMessage += 'No speech was detected';
break;
case 'not-allowed':
errorMessage += 'Microphone access was denied';
break;
default:
errorMessage += 'An unknown error occurred';
}

displayMessage(errorMessage, userElements);
toggleMicrophoneButton(false);
};
} else {
const userElements = prepareMessage('bot');
displayMessage('Speech recognition is not supported by your browser. Please try using a modern browser like Chrome.', userElements);
}
}

function toggleSpeechRecognition() {
if (!recognition) {
initializeSpeechRecognition();
}

if (!isListening) {
try {
recognition.start();
isListening = true;
toggleMicrophoneButton(true);
} catch (error) {
const userElements = prepareMessage('bot');
displayMessage('Could not start speech recognition. Please make sure you have granted microphone permissions.', userElements);
toggleMicrophoneButton(false);
}
} else {
recognition.stop();
isListening = false;
toggleMicrophoneButton(false);
}
}

function toggleMicrophoneButton(isActive) {
const micButton = document.getElementById('mic-button');
if (isActive) {
micButton.classList.add('active');
} else {
micButton.classList.remove('active');
}
}
15 changes: 12 additions & 3 deletions src/main/resources/templates/fragments/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,18 @@
<div class="chatbox-messages" id="chatbox-messages">
<!-- Chat messages will be dynamically inserted here -->
</div>
<div class="chatbox-footer">
<input type="text" id="chatbox-input" placeholder="Type a message..." onkeydown="handleKeyPress(event)" />
<button onclick="sendMessage()">Send</button>
<div id="chatbox-input-container">
<textarea
id="chatbox-input"
rows="1"
onkeydown="handleKeyDown(event)"
placeholder="Enter your message..."></textarea>
<button onclick="sendMessage()" class="btn btn-primary">
<i class="fa fa-paper-plane"></i>
</button>
<button id="mic-button" onclick="toggleSpeechRecognition()" class="btn btn-primary">
<i class="fa fa-microphone"></i>
</button>
</div>
</div>
</div>
Expand Down

0 comments on commit 62e5f5e

Please sign in to comment.