mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-18 20:17:50 +00:00
enhanced ui and upgraded. Now audio in and audio out are working
This commit is contained in:
parent
e43d9cf4b2
commit
dec6811571
@ -1,5 +1,5 @@
|
||||
# =================== Lord Of Large Language Models Configuration file ===========================
|
||||
version: 13
|
||||
version: 14
|
||||
binding_name: null
|
||||
model_name: null
|
||||
|
||||
@ -42,5 +42,6 @@ auto_update: false
|
||||
|
||||
|
||||
# Audio
|
||||
audio_language: 'en-US'
|
||||
audio_in_language: 'en-US'
|
||||
audio_out_voice: null
|
||||
silenceTimer: 5000
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
@ -6,8 +6,8 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LoLLMS WebUI - Welcome</title>
|
||||
<script type="module" crossorigin src="/assets/index-88284c86.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-3c6d3c05.css">
|
||||
<script type="module" crossorigin src="/assets/index-559e0f27.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-ccd4ce08.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
@ -281,7 +281,7 @@ export default {
|
||||
startSpeechRecognition() {
|
||||
if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
|
||||
this.recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
|
||||
this.recognition.lang = this.$store.state.config.audio_language; // Set the language, adjust as needed
|
||||
this.recognition.lang = this.$store.state.config.audio_in_language; // Set the language, adjust as needed
|
||||
this.recognition.interimResults = true; // Enable interim results to get real-time updates
|
||||
|
||||
this.recognition.onstart = () => {
|
||||
|
@ -27,7 +27,14 @@
|
||||
</button>
|
||||
<button
|
||||
@click="validateChoice"
|
||||
class="py-2 px-4 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition duration-300"
|
||||
:class="{
|
||||
'bg-gray-400 cursor-not-allowed': !selectedChoice,
|
||||
'bg-blue-500 hover:bg-blue-600': selectedChoice,
|
||||
'text-white': selectedChoice,
|
||||
'text-gray-500': !selectedChoice,
|
||||
}"
|
||||
:disabled="!selectedChoice"
|
||||
class="py-2 px-4 rounded-lg transition duration-300"
|
||||
>
|
||||
Validate
|
||||
</button>
|
||||
|
@ -115,6 +115,14 @@
|
||||
message.rank }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="text-lg hover:text-red-600 duration-75 active:scale-90 p-2"
|
||||
title="speak"
|
||||
@click.stop="speak()"
|
||||
:class="{ 'text-red-500': isTalking }">
|
||||
<i data-feather="volume-2"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -133,7 +141,7 @@
|
||||
<textarea v-if="editMsgMode" ref="mdTextarea" :rows="4"
|
||||
class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
:style="{ minHeight: mdRenderHeight + `px` }" placeholder="Enter message here..."
|
||||
v-model="new_message_content"></textarea>
|
||||
v-model="this.message.content"></textarea>
|
||||
</div>
|
||||
<!-- FOOTER -->
|
||||
<div class="text-sm text-gray-400 mt-2">
|
||||
@ -189,8 +197,10 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isVoiceActive:false,
|
||||
speechSynthesis: null,
|
||||
voices: [],
|
||||
expanded: false,
|
||||
new_message_content: '',
|
||||
showConfirmation: false,
|
||||
editMsgMode: false,
|
||||
deleteMsgMode: false,
|
||||
@ -198,13 +208,57 @@ export default {
|
||||
|
||||
}
|
||||
}, mounted() {
|
||||
this.new_message_content = this.message.content
|
||||
// Check if speech synthesis is supported by the browser
|
||||
if ('speechSynthesis' in window) {
|
||||
this.speechSynthesis = window.speechSynthesis;
|
||||
|
||||
// Load the available voices
|
||||
this.voices = this.speechSynthesis.getVoices();
|
||||
|
||||
// Make sure the voices are loaded before starting speech synthesis
|
||||
if (this.voices.length === 0) {
|
||||
this.speechSynthesis.addEventListener('voiceschanged', this.onVoicesChanged);
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
console.error('Speech synthesis is not supported in this browser.');
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
this.mdRenderHeight = this.$refs.mdRender.$el.offsetHeight
|
||||
})
|
||||
|
||||
}, methods: {
|
||||
onVoicesChanged() {
|
||||
// This event will be triggered when the voices are loaded
|
||||
this.voices = this.speechSynthesis.getVoices();
|
||||
},
|
||||
speak() {
|
||||
// Assuming you have received the text from your backend and stored it in a variable called "streamedText"
|
||||
const textToSpeak = this.message.content;
|
||||
|
||||
// Create a new SpeechSynthesisUtterance instance
|
||||
const msg = new SpeechSynthesisUtterance();
|
||||
msg.text = textToSpeak;
|
||||
|
||||
// Optionally, you can set the voice and other parameters
|
||||
// For example, to set the voice, assuming you want the first voice available:
|
||||
if (this.voices.length > 0) {
|
||||
msg.voice = this.voices[0];
|
||||
}
|
||||
|
||||
// Set isVoiceActive to true before starting synthesis
|
||||
this.isVoiceActive = true;
|
||||
|
||||
// Listen for the end event to set isVoiceActive to false after synthesis completes
|
||||
msg.addEventListener('end', () => {
|
||||
this.isVoiceActive = false;
|
||||
});
|
||||
|
||||
// Speak the text
|
||||
this.speechSynthesis.speak(msg);
|
||||
},
|
||||
toggleModel() {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
@ -225,14 +279,14 @@ export default {
|
||||
|
||||
},
|
||||
updateMessage() {
|
||||
this.$emit('updateMessage', this.message.id, this.new_message_content)
|
||||
this.$emit('updateMessage', this.message.id, this.message.content)
|
||||
this.editMsgMode = false
|
||||
},
|
||||
resendMessage() {
|
||||
this.$emit('resendMessage', this.message.id, this.new_message_content)
|
||||
this.$emit('resendMessage', this.message.id, this.message.content)
|
||||
},
|
||||
continueMessage() {
|
||||
this.$emit('continueMessage', this.message.id, this.new_message_content)
|
||||
this.$emit('continueMessage', this.message.id, this.message.content)
|
||||
},
|
||||
getImgUrl() {
|
||||
if (this.avatar) {
|
||||
@ -314,10 +368,6 @@ export default {
|
||||
|
||||
editMsgMode(val) {
|
||||
|
||||
if (!val) {
|
||||
this.new_message_content = this.message.content
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
feather.replace()
|
||||
|
||||
@ -332,6 +382,11 @@ export default {
|
||||
|
||||
},
|
||||
computed: {
|
||||
isTalking :{
|
||||
get(){
|
||||
return this.isVoiceActive
|
||||
}
|
||||
},
|
||||
created_at() {
|
||||
return this.prettyDate(this.message.created_at)
|
||||
|
||||
|
@ -667,7 +667,61 @@
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="audio_in_language" class="text-sm font-bold" style="margin-right: 1rem;">Input Audio Language:</label>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Select element for choosing the input audio language -->
|
||||
<select
|
||||
id="audio_in_language"
|
||||
v-model="audio_in_language"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<!-- Options with language codes and corresponding language names -->
|
||||
<option v-for="language in audioLanguages" :key="language.code" :value="language.code">
|
||||
{{ language.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('audio_in_language', audio_in_language)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="min-width: 200px;">
|
||||
<label for="audio_out_voice" class="text-sm font-bold" style="margin-right: 1rem;">Output Audio Voice:</label>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Select element for choosing the output audio voice -->
|
||||
<select
|
||||
id="audio_out_voice"
|
||||
v-model="audio_out_voice"
|
||||
class="mt-1 px-2 py-1 border border-gray-300 rounded"
|
||||
>
|
||||
<!-- Options with available voices in the browser -->
|
||||
<option v-for="voice in audioVoices" :key="voice.name" :value="voice.name">
|
||||
{{ voice.name }}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
class="hover:text-secondary bg-blue-100 m-2 p-2 duration-75 flex justify-center w-full hover:bg-bg-light-tone hover:dark:bg-bg-dark-tone rounded-lg"
|
||||
@click="update_setting('audio_out_voice', audio_out_voice)"
|
||||
>
|
||||
<i data-feather="check"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
<!-- Row 0 -->
|
||||
@ -1525,7 +1579,9 @@ export default {
|
||||
},
|
||||
data() {
|
||||
|
||||
return {
|
||||
return {
|
||||
audioVoices:[],
|
||||
// update
|
||||
has_updates:false,
|
||||
// Variant selection
|
||||
variant_choices:[],
|
||||
@ -1591,6 +1647,18 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
getVoices() {
|
||||
// Fetch available voices from the SpeechSynthesis API
|
||||
if ('speechSynthesis' in window) {
|
||||
speechSynthesis.onvoiceschanged = () => {
|
||||
this.audioVoices = speechSynthesis.getVoices();
|
||||
// Set a default voice if needed
|
||||
if (!this.audio_out_voice && this.audioVoices.length > 0) {
|
||||
this.audio_out_voice = this.audioVoices[0].name;
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
async updateHasUpdates() {
|
||||
let res = await this.api_get_req("check_update");
|
||||
this.has_updates = res["update_availability"];
|
||||
@ -2870,14 +2938,81 @@ export default {
|
||||
|
||||
}, async mounted() {
|
||||
this.constructor()
|
||||
|
||||
console.log("Getting voices")
|
||||
this.getVoices();
|
||||
},
|
||||
activated() {
|
||||
if (this.isMounted) {
|
||||
this.constructor()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computed: {
|
||||
audio_out_voice:{
|
||||
get() {
|
||||
return this.$store.state.config.audio_out_voice;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.state.config.audio_out_voice = value;
|
||||
},
|
||||
},
|
||||
|
||||
audioLanguages() {
|
||||
// Replace this with your own list of language codes and names
|
||||
// Example data structure: [{ code: 'en-US', name: 'English (US)' }, ...]
|
||||
return [
|
||||
{ code: 'en-US', name: 'English (US)' },
|
||||
{ code: 'en-GB', name: 'English (UK)' },
|
||||
{ code: 'es-ES', name: 'Spanish (Spain)' },
|
||||
{ code: 'es-MX', name: 'Spanish (Mexico)' },
|
||||
{ code: 'fr-FR', name: 'French (France)' },
|
||||
{ code: 'fr-CA', name: 'French (Canada)' },
|
||||
{ code: 'de-DE', name: 'German (Germany)' },
|
||||
{ code: 'it-IT', name: 'Italian (Italy)' },
|
||||
{ code: 'pt-BR', name: 'Portuguese (Brazil)' },
|
||||
{ code: 'pt-PT', name: 'Portuguese (Portugal)' },
|
||||
{ code: 'ru-RU', name: 'Russian (Russia)' },
|
||||
{ code: 'zh-CN', name: 'Chinese (China)' },
|
||||
{ code: 'ja-JP', name: 'Japanese (Japan)' },
|
||||
{ code: 'ar-SA', name: 'Arabic (Saudi Arabia)' },
|
||||
{ code: 'tr-TR', name: 'Turkish (Turkey)' },
|
||||
{ code: 'ms-MY', name: 'Malay (Malaysia)' },
|
||||
{ code: 'ko-KR', name: 'Korean (South Korea)' },
|
||||
{ code: 'nl-NL', name: 'Dutch (Netherlands)' },
|
||||
{ code: 'sv-SE', name: 'Swedish (Sweden)' },
|
||||
{ code: 'da-DK', name: 'Danish (Denmark)' },
|
||||
{ code: 'fi-FI', name: 'Finnish (Finland)' },
|
||||
{ code: 'no-NO', name: 'Norwegian (Norway)' },
|
||||
{ code: 'pl-PL', name: 'Polish (Poland)' },
|
||||
{ code: 'el-GR', name: 'Greek (Greece)' },
|
||||
{ code: 'hu-HU', name: 'Hungarian (Hungary)' },
|
||||
{ code: 'cs-CZ', name: 'Czech (Czech Republic)' },
|
||||
{ code: 'th-TH', name: 'Thai (Thailand)' },
|
||||
{ code: 'hi-IN', name: 'Hindi (India)' },
|
||||
{ code: 'he-IL', name: 'Hebrew (Israel)' },
|
||||
{ code: 'id-ID', name: 'Indonesian (Indonesia)' },
|
||||
{ code: 'vi-VN', name: 'Vietnamese (Vietnam)' },
|
||||
{ code: 'uk-UA', name: 'Ukrainian (Ukraine)' },
|
||||
{ code: 'ro-RO', name: 'Romanian (Romania)' },
|
||||
{ code: 'bg-BG', name: 'Bulgarian (Bulgaria)' },
|
||||
{ code: 'hr-HR', name: 'Croatian (Croatia)' },
|
||||
{ code: 'sr-RS', name: 'Serbian (Serbia)' },
|
||||
{ code: 'sk-SK', name: 'Slovak (Slovakia)' },
|
||||
{ code: 'sl-SI', name: 'Slovenian (Slovenia)' },
|
||||
{ code: 'et-EE', name: 'Estonian (Estonia)' },
|
||||
{ code: 'lv-LV', name: 'Latvian (Latvia)' },
|
||||
{ code: 'lt-LT', name: 'Lithuanian (Lithuania)' },
|
||||
{ code: 'ka-GE', name: 'Georgian (Georgia)' },
|
||||
{ code: 'hy-AM', name: 'Armenian (Armenia)' },
|
||||
{ code: 'az-AZ', name: 'Azerbaijani (Azerbaijan)' },
|
||||
{ code: 'kk-KZ', name: 'Kazakh (Kazakhstan)' },
|
||||
{ code: 'uz-UZ', name: 'Uzbek (Uzbekistan)' },
|
||||
{ code: 'kkj-CM', name: 'Kako (Cameroon)' },
|
||||
{ code: 'my-MM', name: 'Burmese (Myanmar)' },
|
||||
{ code: 'ne-NP', name: 'Nepali (Nepal)' },
|
||||
{ code: 'si-LK', name: 'Sinhala (Sri Lanka)' },
|
||||
// Add more language entries as needed
|
||||
];
|
||||
},
|
||||
configFile: {
|
||||
get() {
|
||||
return this.$store.state.config;
|
||||
@ -2925,6 +3060,15 @@ export default {
|
||||
},
|
||||
|
||||
},
|
||||
audio_in_language:{
|
||||
get() {
|
||||
return this.$store.state.config.audio_in_language;
|
||||
},
|
||||
set(value) {
|
||||
// You should not set the value directly here; use the updateSetting method instead
|
||||
this.$store.state.config.audio_in_language = value
|
||||
},
|
||||
},
|
||||
|
||||
use_user_name_in_discussions: {
|
||||
get() {
|
||||
|
Loading…
Reference in New Issue
Block a user