salomonsky commited on
Commit
2e947b2
verified
1 Parent(s): c1c3e23

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. chat.js +288 -0
  2. main.js +384 -0
chat.js ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Variables globales
2
+ let currentMode = 'seguros';
3
+ let currentModel = 'Gemini 8b';
4
+ let currentTTS = 'EDGE';
5
+ let isListening = false;
6
+ let recognition = null;
7
+
8
+ function playAudio(button, text) {
9
+ const icon = button ? button.querySelector('i') : null;
10
+ text = text || (button ? button.getAttribute('data-text') : '');
11
+
12
+ if (!text) {
13
+ console.error('No text provided for audio');
14
+ return;
15
+ }
16
+
17
+ if (button) {
18
+ icon.className = 'fas fa-spinner fa-spin';
19
+ button.disabled = true;
20
+ }
21
+
22
+ fetch('/generate_audio', {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ body: JSON.stringify({
28
+ text: text,
29
+ model: currentTTS
30
+ })
31
+ })
32
+ .then(response => {
33
+ if (!response.ok) {
34
+ throw new Error(`HTTP error! status: ${response.status}`);
35
+ }
36
+ return response.json();
37
+ })
38
+ .then(data => {
39
+ if (data.audio_url) {
40
+ console.log(`Reproduciendo audio generado con modelo: ${data.model_used}`);
41
+ const audio = new Audio(window.location.origin + data.audio_url);
42
+
43
+ if (button) {
44
+ audio.onplay = () => {
45
+ icon.className = 'fas fa-pause';
46
+ };
47
+
48
+ audio.onended = () => {
49
+ icon.className = 'fas fa-play';
50
+ button.disabled = false;
51
+ };
52
+ }
53
+
54
+ audio.onerror = (e) => {
55
+ console.error('Error reproduciendo audio:', e);
56
+ if (button) {
57
+ icon.className = 'fas fa-play';
58
+ button.disabled = false;
59
+ }
60
+ appendBotMessage("Error reproduciendo el audio. Intente de nuevo.");
61
+ };
62
+
63
+ audio.play().catch(error => {
64
+ console.error('Error playing audio:', error);
65
+ if (button) {
66
+ icon.className = 'fas fa-play';
67
+ button.disabled = false;
68
+ }
69
+ appendBotMessage("Error reproduciendo el audio. Intente de nuevo.");
70
+ });
71
+ } else {
72
+ throw new Error('No audio URL in response');
73
+ }
74
+ })
75
+ .catch(error => {
76
+ console.error('Error:', error);
77
+ if (button) {
78
+ icon.className = 'fas fa-play';
79
+ button.disabled = false;
80
+ }
81
+ appendBotMessage(`Error generando el audio: ${error.message}`);
82
+ });
83
+ }
84
+
85
+ // Agregar esto para manejar nuevos mensajes din谩micamente
86
+ function appendBotMessage(message) {
87
+ const chatMessages = document.getElementById('chat-messages');
88
+ const messageDiv = document.createElement('div');
89
+ messageDiv.className = 'message bot-message';
90
+ messageDiv.innerHTML = `
91
+ <div class="message-content">${message}</div>
92
+ <button onclick="playAudio(this)" data-text="${message}" class="play-button">
93
+ <i class="fas fa-play"></i>
94
+ </button>
95
+ `;
96
+ chatMessages.appendChild(messageDiv);
97
+ }
98
+
99
+ async function sendMessage() {
100
+ const input = document.getElementById('user-input');
101
+ const text = input.value.trim();
102
+
103
+ if (text) {
104
+ // Agregar mensaje del usuario
105
+ const chatMessages = document.getElementById('chat-messages');
106
+ const userDiv = document.createElement('div');
107
+ userDiv.className = 'message user-message';
108
+ userDiv.textContent = text;
109
+ chatMessages.appendChild(userDiv);
110
+
111
+ // Limpiar input
112
+ input.value = '';
113
+
114
+ try {
115
+ // Enviar mensaje al backend con el modo actual
116
+ const response = await fetch('/chat', {
117
+ method: 'POST',
118
+ headers: {
119
+ 'Content-Type': 'application/json',
120
+ },
121
+ body: JSON.stringify({
122
+ message: text,
123
+ mode: currentMode
124
+ })
125
+ });
126
+
127
+ const data = await response.json();
128
+
129
+ if (data.response) {
130
+ // Agregar respuesta del bot
131
+ appendBotMessage(data.response);
132
+ // Auto-reproducir respuesta
133
+ const lastButton = document.querySelector('.bot-message:last-child .play-button');
134
+ if (lastButton) {
135
+ playAudio(lastButton);
136
+ }
137
+ }
138
+ } catch (error) {
139
+ console.error('Error:', error);
140
+ appendBotMessage("Lo siento, hubo un error al procesar tu mensaje.");
141
+ }
142
+ }
143
+ }
144
+
145
+ async function changeMode(mode) {
146
+ try {
147
+ const response = await fetch('/change_mode', {
148
+ method: 'POST',
149
+ headers: {
150
+ 'Content-Type': 'application/json',
151
+ },
152
+ body: JSON.stringify({ mode })
153
+ });
154
+
155
+ if (response.ok) {
156
+ currentMode = mode;
157
+ appendBotMessage(`Modo cambiado a: ${mode}`);
158
+ playAudio(null, `Modo de operaci贸n cambiado a ${mode}`);
159
+ }
160
+ } catch (error) {
161
+ console.error('Error changing mode:', error);
162
+ appendBotMessage("Error al cambiar el modo de operaci贸n");
163
+ }
164
+ }
165
+
166
+ async function changeModel(model) {
167
+ try {
168
+ const response = await fetch('/change_model', {
169
+ method: 'POST',
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ },
173
+ body: JSON.stringify({ model })
174
+ });
175
+
176
+ if (response.ok) {
177
+ currentModel = model;
178
+ appendBotMessage(`Modelo de IA cambiado a: ${model}`);
179
+ playAudio(null, `Modelo de inteligencia artificial cambiado a ${model}`);
180
+ }
181
+ } catch (error) {
182
+ console.error('Error changing model:', error);
183
+ appendBotMessage("Error al cambiar el modelo de IA");
184
+ }
185
+ }
186
+
187
+ // Inicializar reconocimiento de voz
188
+ function initSpeechRecognition() {
189
+ if ('webkitSpeechRecognition' in window) {
190
+ recognition = new webkitSpeechRecognition();
191
+ recognition.continuous = true;
192
+ recognition.interimResults = true;
193
+ recognition.lang = 'es-ES';
194
+
195
+ recognition.onresult = function(event) {
196
+ const result = event.results[event.results.length - 1];
197
+ if (result.isFinal) {
198
+ const text = result.item(0).transcript;
199
+ document.getElementById('user-input').value = text;
200
+ sendMessage();
201
+ }
202
+ };
203
+
204
+ recognition.onerror = function(event) {
205
+ console.error('Error en reconocimiento:', event.error);
206
+ toggleVAD();
207
+ };
208
+ } else {
209
+ console.error('Reconocimiento de voz no soportado');
210
+ }
211
+ }
212
+
213
+ // Funci贸n para cambiar TTS
214
+ async function changeTTS(model) {
215
+ try {
216
+ const response = await fetch('/change_tts', {
217
+ method: 'POST',
218
+ headers: {
219
+ 'Content-Type': 'application/json',
220
+ },
221
+ body: JSON.stringify({ model })
222
+ });
223
+
224
+ if (response.ok) {
225
+ currentTTS = model;
226
+ appendBotMessage(`Modelo de voz cambiado a: ${model}`);
227
+ playAudio(null, `Voz del sistema cambiada a ${model}`);
228
+ }
229
+ } catch (error) {
230
+ console.error('Error changing TTS:', error);
231
+ appendBotMessage("Error al cambiar el modelo de voz");
232
+ }
233
+ }
234
+
235
+ // Funci贸n para activar/desactivar VAD
236
+ function toggleVAD() {
237
+ const vadButton = document.querySelector('.vad-button');
238
+ const vadStatus = document.getElementById('vad-status');
239
+
240
+ if (!isListening) {
241
+ if (recognition) {
242
+ recognition.start();
243
+ isListening = true;
244
+ vadButton.classList.remove('inactive');
245
+ vadButton.classList.add('active');
246
+ vadButton.innerHTML = '<i class="fas fa-microphone"></i> Escuchando';
247
+
248
+ // Mostrar estado de escucha
249
+ vadStatus.classList.add('listening');
250
+ vadStatus.innerHTML = '馃帳 Escuchando...';
251
+
252
+ // Reproducir sonido de inicio
253
+ playAudio(null, "Sistema activado. Puede hablar.");
254
+ }
255
+ } else {
256
+ if (recognition) {
257
+ recognition.stop();
258
+ isListening = false;
259
+ vadButton.classList.remove('active');
260
+ vadButton.classList.add('inactive');
261
+ vadButton.innerHTML = '<i class="fas fa-microphone-slash"></i> Activar micr贸fono';
262
+
263
+ // Ocultar estado de escucha
264
+ vadStatus.classList.remove('listening');
265
+ vadStatus.innerHTML = '';
266
+
267
+ // Mensaje de desactivaci贸n
268
+ playAudio(null, "Sistema en pausa.");
269
+ }
270
+ }
271
+ }
272
+
273
+ // Inicializar cuando el documento est茅 listo
274
+ document.addEventListener('DOMContentLoaded', function() {
275
+ initSpeechRecognition();
276
+ // Iniciar VAD autom谩ticamente
277
+ setTimeout(() => {
278
+ toggleVAD(); // Activar el VAD al inicio
279
+ appendBotMessage("Sistema iniciado. Modelos cargados correctamente. Escuchando...");
280
+ }, 1000);
281
+ });
282
+
283
+ // Permitir enviar con Enter
284
+ document.getElementById('user-input')?.addEventListener('keypress', function(e) {
285
+ if (e.key === 'Enter') {
286
+ sendMessage();
287
+ }
288
+ });
main.js ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let recognition = null;
2
+ let isProcessingSpeech = false;
3
+ let isPlayingAudio = false;
4
+ const STOP_WORDS = ['alto', 'detente', 'permiteme', 'callate', 'silencio'];
5
+
6
+ // Elementos del DOM
7
+ const chatBox = document.getElementById('chatBox');
8
+ const textInput = document.getElementById('textInput');
9
+ const sendTextButton = document.getElementById('sendText');
10
+ const modoSelect = document.getElementById('modoSelect');
11
+ const modeloSelect = document.getElementById('modeloSelect');
12
+ const vozSelect = document.getElementById('vozSelect');
13
+ const configForm = document.getElementById('configForm');
14
+ const statusLabel = document.getElementById('statusLabel');
15
+
16
+ // Manejar guardado de configuraci贸n
17
+ if (configForm) {
18
+ configForm.addEventListener('submit', async (e) => {
19
+ e.preventDefault();
20
+ const config = {
21
+ GOOGLE_API_KEY: document.getElementById('googleApiKey').value,
22
+ HUGGINGFACE_TOKEN: document.getElementById('huggingfaceToken').value,
23
+ OPENAI_API_KEY: document.getElementById('openaiApiKey').value
24
+ };
25
+
26
+ try {
27
+ const response = await fetch('/guardar_config', {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Content-Type': 'application/json'
31
+ },
32
+ body: JSON.stringify(config)
33
+ });
34
+
35
+ const data = await response.json();
36
+ if (data.success) {
37
+ alert('Configuraci贸n guardada exitosamente');
38
+ } else {
39
+ alert('Error al guardar la configuraci贸n: ' + data.error);
40
+ }
41
+ } catch (error) {
42
+ alert('Error al guardar la configuraci贸n: ' + error);
43
+ }
44
+ });
45
+ }
46
+
47
+ // Manejar cambio de modelo AI
48
+ if (modeloSelect) {
49
+ modeloSelect.addEventListener('change', async function() {
50
+ const response = await fetch('/cambiar_modelo', {
51
+ method: 'POST',
52
+ headers: {
53
+ 'Content-Type': 'application/json'
54
+ },
55
+ body: JSON.stringify({ modelo: this.value })
56
+ });
57
+
58
+ const data = await response.json();
59
+ if (!data.success) {
60
+ alert('Error al cambiar el modelo: ' + data.error);
61
+ }
62
+ });
63
+ }
64
+
65
+ // Manejar cambio de modelo de voz
66
+ if (vozSelect) {
67
+ vozSelect.addEventListener('change', async function() {
68
+ const response = await fetch('/cambiar_voz', {
69
+ method: 'POST',
70
+ headers: {
71
+ 'Content-Type': 'application/json'
72
+ },
73
+ body: JSON.stringify({ voz: this.value })
74
+ });
75
+
76
+ const data = await response.json();
77
+ if (!data.success) {
78
+ alert('Error al cambiar el modelo de voz: ' + data.error);
79
+ }
80
+ });
81
+ }
82
+
83
+ // Manejar cambio de modo
84
+ modoSelect.addEventListener('change', async function() {
85
+ const response = await fetch('/cambiar_modo', {
86
+ method: 'POST',
87
+ headers: {
88
+ 'Content-Type': 'application/json'
89
+ },
90
+ body: JSON.stringify({ modo: this.value })
91
+ });
92
+
93
+ const data = await response.json();
94
+ if (data.success) {
95
+ addMessage(data.mensaje, 'bot');
96
+ if (data.audio) {
97
+ await playAudio(data.audio);
98
+ }
99
+ updateStatus('Esperando activaci贸n...');
100
+ }
101
+ });
102
+
103
+ // Funci贸n para verificar si el texto contiene palabras de parada
104
+ function containsStopWord(text) {
105
+ const lowerText = text.toLowerCase();
106
+ return STOP_WORDS.some(word => lowerText.includes(word));
107
+ }
108
+
109
+ // Funci贸n para reproducir audio
110
+ async function playAudio(base64Audio) {
111
+ try {
112
+ console.log('Preparando reproducci贸n de audio...');
113
+ isPlayingAudio = true;
114
+
115
+ // Detener completamente el reconocimiento
116
+ if (recognition) {
117
+ recognition.stop();
118
+ recognition.abort();
119
+ await new Promise(resolve => setTimeout(resolve, 500));
120
+ }
121
+
122
+ updateStatus('Reproduciendo respuesta...');
123
+ const audio = new Audio('data:audio/mp3;base64,' + base64Audio);
124
+
125
+ // Permitir interrupciones durante la reproducci贸n
126
+ recognition.start();
127
+
128
+ audio.onended = () => {
129
+ console.log('Audio reproducido completamente');
130
+ isPlayingAudio = false;
131
+ updateStatus('Escuchando...');
132
+ };
133
+
134
+ audio.play();
135
+
136
+ } catch (error) {
137
+ console.error('Error reproduciendo audio:', error);
138
+ isPlayingAudio = false;
139
+ updateStatus('Error al reproducir audio. Escuchando...');
140
+ if (!isProcessingSpeech) {
141
+ recognition.start();
142
+ }
143
+ }
144
+ }
145
+
146
+ // Funci贸n para agregar mensaje al chat
147
+ function addMessage(text, sender) {
148
+ if (!text || !sender) {
149
+ console.error('Error: texto o remitente faltante', { text, sender });
150
+ return;
151
+ }
152
+
153
+ const chatBox = document.getElementById('chatBox');
154
+ if (!chatBox) {
155
+ console.error('Error cr铆tico: No se encontr贸 el elemento chatBox');
156
+ return;
157
+ }
158
+
159
+ try {
160
+ console.log('Agregando mensaje al chat:', { text, sender });
161
+
162
+ const messageDiv = document.createElement('div');
163
+ messageDiv.className = `message ${sender}-message`;
164
+
165
+ const iconSpan = document.createElement('span');
166
+ iconSpan.className = 'message-icon';
167
+ iconSpan.textContent = sender === 'user' ? '馃懁' : '馃';
168
+
169
+ const textSpan = document.createElement('span');
170
+ textSpan.className = 'message-text';
171
+ textSpan.textContent = text;
172
+
173
+ messageDiv.appendChild(iconSpan);
174
+ messageDiv.appendChild(textSpan);
175
+ chatBox.appendChild(messageDiv);
176
+
177
+ // Hacer scroll al 煤ltimo mensaje
178
+ chatBox.scrollTop = chatBox.scrollHeight;
179
+
180
+ console.log('Mensaje agregado exitosamente');
181
+ } catch (error) {
182
+ console.error('Error al agregar mensaje:', error);
183
+ }
184
+ }
185
+
186
+ // Agregar m煤ltiples mensajes
187
+ function addMessages(messages) {
188
+ messages.forEach(msg => {
189
+ addMessage(msg.text, msg.sender);
190
+ });
191
+ }
192
+
193
+ // Funci贸n para actualizar el estado
194
+ function updateStatus(text) {
195
+ const statusLabel = document.getElementById('statusLabel');
196
+ if (statusLabel) {
197
+ statusLabel.textContent = text;
198
+ statusLabel.style.display = 'block'; // Asegurar que sea visible
199
+ console.log('Estado actualizado:', text);
200
+ } else {
201
+ console.error('No se encontr贸 el elemento statusLabel');
202
+ }
203
+ }
204
+
205
+ // Funci贸n para procesar la respuesta del servidor
206
+ async function processServerResponse(data, userText) {
207
+ try {
208
+ console.log('Procesando respuesta del servidor:', data);
209
+
210
+ // Mostrar el mensaje del usuario
211
+ addMessage(userText, 'user');
212
+
213
+ if (data.success && data.texto) {
214
+ // Mostrar la respuesta del bot
215
+ console.log('Mostrando respuesta del bot:', data.texto);
216
+ addMessage(data.texto, 'bot');
217
+
218
+ // Reproducir el audio si existe y no se dijo una palabra de parada
219
+ if (data.audio && !containsStopWord(userText)) {
220
+ await playAudio(data.audio);
221
+ }
222
+ } else {
223
+ const errorMessage = data.error || 'Error desconocido';
224
+ console.error('Error en la respuesta:', errorMessage);
225
+ addMessage('Lo siento, hubo un error: ' + errorMessage, 'bot');
226
+ }
227
+ } catch (error) {
228
+ console.error('Error procesando respuesta:', error);
229
+ addMessage('Lo siento, ocurri贸 un error inesperado.', 'bot');
230
+ }
231
+ }
232
+
233
+ // Inicializar reconocimiento de voz
234
+ function initializeSpeechRecognition() {
235
+ if ('webkitSpeechRecognition' in window) {
236
+ recognition = new webkitSpeechRecognition();
237
+ recognition.continuous = true; // Cambiar a true para permitir interrupciones
238
+ recognition.interimResults = true;
239
+ recognition.lang = 'es-ES';
240
+
241
+ recognition.onstart = function() {
242
+ console.log('Reconocimiento de voz iniciado');
243
+ updateStatus('Escuchando...');
244
+ };
245
+
246
+ recognition.onend = function() {
247
+ console.log('Reconocimiento de voz terminado');
248
+ if (!isProcessingSpeech) {
249
+ console.log('Reiniciando reconocimiento...');
250
+ updateStatus('Escuchando...');
251
+ setTimeout(() => recognition.start(), 500);
252
+ }
253
+ };
254
+
255
+ recognition.onerror = function(event) {
256
+ console.error('Error en reconocimiento de voz:', event.error);
257
+ if (!isProcessingSpeech && event.error !== 'no-speech') {
258
+ setTimeout(() => recognition.start(), 1000);
259
+ }
260
+ };
261
+
262
+ recognition.onresult = async function(event) {
263
+ let finalTranscript = '';
264
+ let interimTranscript = '';
265
+
266
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
267
+ const transcript = event.results[i][0].transcript;
268
+
269
+ if (event.results[i].isFinal) {
270
+ finalTranscript = transcript;
271
+ console.log('Texto final:', finalTranscript);
272
+
273
+ // Verificar si es una palabra de parada
274
+ if (containsStopWord(finalTranscript)) {
275
+ console.log('Palabra de parada detectada, deteniendo audio...');
276
+ const audio = document.querySelector('audio');
277
+ if (audio) {
278
+ audio.pause();
279
+ audio.currentTime = 0;
280
+ }
281
+ isPlayingAudio = false;
282
+ updateStatus('Audio detenido. Escuchando...');
283
+ continue;
284
+ }
285
+
286
+ if (!isProcessingSpeech) {
287
+ isProcessingSpeech = true;
288
+ updateStatus('Procesando...');
289
+
290
+ try {
291
+ console.log('Enviando texto al servidor:', finalTranscript);
292
+ const response = await fetch('/procesar_voz', {
293
+ method: 'POST',
294
+ headers: {
295
+ 'Content-Type': 'application/json'
296
+ },
297
+ body: JSON.stringify({ texto: finalTranscript })
298
+ });
299
+
300
+ const data = await response.json();
301
+ console.log('Respuesta recibida:', data);
302
+ await processServerResponse(data, finalTranscript);
303
+
304
+ } catch (error) {
305
+ console.error('Error al procesar voz:', error);
306
+ updateStatus('Error de conexi贸n. Escuchando...');
307
+ addMessage('Lo siento, hubo un error de conexi贸n.', 'bot');
308
+ }
309
+
310
+ isProcessingSpeech = false;
311
+ }
312
+ } else {
313
+ interimTranscript = transcript;
314
+ updateStatus(`Escuchando: ${interimTranscript}`);
315
+ }
316
+ }
317
+ };
318
+
319
+ // Iniciar reconocimiento
320
+ recognition.start();
321
+ updateStatus('Escuchando...');
322
+
323
+ } else {
324
+ console.error('El reconocimiento de voz no est谩 soportado en este navegador');
325
+ alert('Tu navegador no soporta el reconocimiento de voz. Por favor, usa Chrome.');
326
+ updateStatus('Reconocimiento de voz no soportado');
327
+ }
328
+ }
329
+
330
+ // Manejar env铆o de texto
331
+ sendTextButton.addEventListener('click', async () => {
332
+ const text = textInput.value.trim();
333
+ if (text) {
334
+ updateStatus('Procesando...');
335
+ addMessage(text, 'user');
336
+
337
+ try {
338
+ const response = await fetch('/procesar_voz', {
339
+ method: 'POST',
340
+ headers: {
341
+ 'Content-Type': 'application/json'
342
+ },
343
+ body: JSON.stringify({ texto: text })
344
+ });
345
+
346
+ const data = await response.json();
347
+ await processServerResponse(data, text);
348
+
349
+ textInput.value = '';
350
+ updateStatus('Esperando activaci贸n...');
351
+ } catch (error) {
352
+ console.error('Error:', error);
353
+ updateStatus('Error de conexi贸n');
354
+ }
355
+ }
356
+ });
357
+
358
+ // Inicializar cuando se carga la p谩gina
359
+ document.addEventListener('DOMContentLoaded', () => {
360
+ console.log('P谩gina cargada, verificando elementos...');
361
+
362
+ // Verificar que existan los elementos necesarios
363
+ const chatBox = document.getElementById('chatBox');
364
+ const statusLabel = document.getElementById('statusLabel');
365
+
366
+ if (!chatBox) {
367
+ console.error('Error cr铆tico: No se encontr贸 el elemento chatBox');
368
+ return;
369
+ }
370
+
371
+ if (!statusLabel) {
372
+ console.error('Error cr铆tico: No se encontr贸 el elemento statusLabel');
373
+ return;
374
+ }
375
+
376
+ // Asegurar que el statusLabel sea visible
377
+ statusLabel.style.display = 'block';
378
+
379
+ console.log('Elementos encontrados, inicializando reconocimiento de voz...');
380
+ initializeSpeechRecognition();
381
+
382
+ // Agregar mensaje de bienvenida
383
+ addMessage('隆Hola! Soy tu asistente virtual. 驴En qu茅 puedo ayudarte?', 'bot');
384
+ });