Spaces:
Configuration error
Configuration error
Bonjour,
Browse filesJ'aimerais avec grand respect vous demander de réactiver mon thème personnalisé que nous avons créé ensemble. Je tiens beaucoup à ce projet et je crois en notre collaboration.
Pourriez-vous également veiller à ces détails importants :
Micro fonctionnel dans la barre de recherche pour l'envoi vocal
Bonne ergonomie pour l'envoi de fichiers
Interface claire comme sur la capture d'écran
Je suis ouvert à discuter pour améliorer notre façon de travailler ensemble en équipe. Je crois que nous pouvons créer quelque chose de vraiment génial si nous communiquons bien et que nous nous respectons mutuellement.
Avec confiance et en attendant une réponse positive,
- components/chat.js +92 -50
- style.css +9 -2
components/chat.js
CHANGED
|
@@ -204,38 +204,56 @@ connectedCallback() {
|
|
| 204 |
const micStatus = shadow.getElementById('micStatus');
|
| 205 |
|
| 206 |
let recognition;
|
| 207 |
-
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
fileButton.addEventListener('click', () => fileInput.click());
|
| 210 |
fileInput.addEventListener('change', (e) => {
|
| 211 |
this._files = Array.from(e.target.files);
|
| 212 |
-
|
| 213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
});
|
| 215 |
-
|
| 216 |
-
// Connect apps
|
| 217 |
connectButton.addEventListener('click', () => {
|
| 218 |
this._addMessage('assistant',
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
'
|
|
|
|
|
|
|
|
|
|
| 222 |
});
|
| 223 |
-
|
| 224 |
-
// Speech recognition
|
| 225 |
micButton.addEventListener('click', () => {
|
| 226 |
-
if (
|
| 227 |
if (recognition) recognition.stop();
|
| 228 |
-
|
| 229 |
micButton.classList.remove('listening');
|
|
|
|
| 230 |
micStatus.textContent = 'En ligne';
|
| 231 |
return;
|
| 232 |
}
|
| 233 |
-
|
| 234 |
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
| 235 |
if (!SpeechRecognition) {
|
| 236 |
-
this.
|
| 237 |
-
micStatus.textContent = 'Micro non supporté';
|
| 238 |
-
setTimeout(() => micStatus.textContent = 'En ligne', 2000);
|
| 239 |
return;
|
| 240 |
}
|
| 241 |
|
|
@@ -245,40 +263,52 @@ connectedCallback() {
|
|
| 245 |
recognition.continuous = true;
|
| 246 |
|
| 247 |
recognition.onstart = () => {
|
| 248 |
-
|
| 249 |
micButton.classList.add('listening');
|
| 250 |
-
|
|
|
|
|
|
|
| 251 |
};
|
| 252 |
-
|
| 253 |
recognition.onresult = (event) => {
|
| 254 |
-
let
|
|
|
|
|
|
|
| 255 |
for (let i = event.resultIndex; i < event.results.length; i++) {
|
| 256 |
-
transcript
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
}
|
| 258 |
-
|
|
|
|
| 259 |
};
|
| 260 |
-
|
| 261 |
recognition.onerror = (event) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
if (event.error === 'not-allowed') {
|
| 263 |
-
|
| 264 |
-
|
| 265 |
} else {
|
| 266 |
-
this._speechState = 'idle';
|
| 267 |
micStatus.textContent = 'Erreur micro';
|
|
|
|
| 268 |
}
|
| 269 |
-
|
| 270 |
-
setTimeout(() => micStatus.textContent = 'En ligne',
|
| 271 |
};
|
| 272 |
|
| 273 |
recognition.onend = () => {
|
| 274 |
-
if (
|
| 275 |
-
|
| 276 |
micButton.classList.remove('listening');
|
|
|
|
| 277 |
micStatus.textContent = 'En ligne';
|
| 278 |
}
|
| 279 |
};
|
| 280 |
-
|
| 281 |
-
try {
|
| 282 |
recognition.start();
|
| 283 |
} catch (err) {
|
| 284 |
this._speechState = 'idle';
|
|
@@ -286,34 +316,46 @@ connectedCallback() {
|
|
| 286 |
micButton.classList.remove('listening');
|
| 287 |
}
|
| 288 |
});
|
| 289 |
-
|
| 290 |
-
// Send message
|
| 291 |
const sendMessage = () => {
|
| 292 |
const message = messageInput.value.trim();
|
| 293 |
if (message || this._files.length) {
|
| 294 |
-
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
-
this._addMessage('user',
|
| 298 |
messageInput.value = '';
|
| 299 |
this._files = [];
|
| 300 |
-
filesInfo.
|
| 301 |
fileInput.value = '';
|
| 302 |
|
| 303 |
-
//
|
| 304 |
setTimeout(() => {
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
"
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
}
|
| 315 |
};
|
| 316 |
-
|
| 317 |
messageInput.addEventListener('keydown', (e) => {
|
| 318 |
if (e.key === 'Enter') sendMessage();
|
| 319 |
});
|
|
|
|
| 204 |
const micStatus = shadow.getElementById('micStatus');
|
| 205 |
|
| 206 |
let recognition;
|
| 207 |
+
let isListening = false;
|
| 208 |
+
|
| 209 |
+
// Style micro actif
|
| 210 |
+
micButton.addEventListener('mouseenter', () => {
|
| 211 |
+
if (isListening) micButton.style.backgroundColor = '#ef4444';
|
| 212 |
+
});
|
| 213 |
+
micButton.addEventListener('mouseleave', () => {
|
| 214 |
+
if (isListening) micButton.style.backgroundColor = '#3b82f6';
|
| 215 |
+
});
|
| 216 |
+
// File upload with preview
|
| 217 |
fileButton.addEventListener('click', () => fileInput.click());
|
| 218 |
fileInput.addEventListener('change', (e) => {
|
| 219 |
this._files = Array.from(e.target.files);
|
| 220 |
+
if (this._files.length) {
|
| 221 |
+
filesInfo.innerHTML = `
|
| 222 |
+
<div style="display:flex; gap:0.5rem; align-items:center;">
|
| 223 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
| 224 |
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
| 225 |
+
<polyline points="14 2 14 8 20 8" />
|
| 226 |
+
</svg>
|
| 227 |
+
${this._files.length} fichier(s) prêt(s) à envoyer
|
| 228 |
+
</div>
|
| 229 |
+
`;
|
| 230 |
+
} else {
|
| 231 |
+
filesInfo.textContent = '';
|
| 232 |
+
}
|
| 233 |
});
|
| 234 |
+
// Connect apps with better UX
|
|
|
|
| 235 |
connectButton.addEventListener('click', () => {
|
| 236 |
this._addMessage('assistant',
|
| 237 |
+
`🔌 Connexion d'applications disponible pour:
|
| 238 |
+
- Stockage local (${navigator.storage ? '✓' : '✗'})
|
| 239 |
+
- Microphone (${'webkitSpeechRecognition' in window ? '✓' : '✗'})
|
| 240 |
+
- Caméra (${navigator.mediaDevices ? '✓' : '✗'})
|
| 241 |
+
|
| 242 |
+
Dites-moi ce que vous souhaitez connecter.`);
|
| 243 |
});
|
| 244 |
+
// Enhanced speech recognition
|
|
|
|
| 245 |
micButton.addEventListener('click', () => {
|
| 246 |
+
if (isListening) {
|
| 247 |
if (recognition) recognition.stop();
|
| 248 |
+
isListening = false;
|
| 249 |
micButton.classList.remove('listening');
|
| 250 |
+
micButton.style.backgroundColor = '';
|
| 251 |
micStatus.textContent = 'En ligne';
|
| 252 |
return;
|
| 253 |
}
|
|
|
|
| 254 |
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
| 255 |
if (!SpeechRecognition) {
|
| 256 |
+
this._addMessage('assistant', 'Votre navigateur ne supporte pas la reconnaissance vocale. Essayez avec Chrome ou Edge.');
|
|
|
|
|
|
|
| 257 |
return;
|
| 258 |
}
|
| 259 |
|
|
|
|
| 263 |
recognition.continuous = true;
|
| 264 |
|
| 265 |
recognition.onstart = () => {
|
| 266 |
+
isListening = true;
|
| 267 |
micButton.classList.add('listening');
|
| 268 |
+
micButton.style.backgroundColor = '#3b82f6';
|
| 269 |
+
micStatus.textContent = '🎤 En écoute...';
|
| 270 |
+
this._addMessage('assistant', 'Je vous écoute... Parlez maintenant.');
|
| 271 |
};
|
|
|
|
| 272 |
recognition.onresult = (event) => {
|
| 273 |
+
let finalTranscript = '';
|
| 274 |
+
let interimTranscript = '';
|
| 275 |
+
|
| 276 |
for (let i = event.resultIndex; i < event.results.length; i++) {
|
| 277 |
+
const transcript = event.results[i][0].transcript;
|
| 278 |
+
if (event.results[i].isFinal) {
|
| 279 |
+
finalTranscript += transcript;
|
| 280 |
+
} else {
|
| 281 |
+
interimTranscript += transcript;
|
| 282 |
+
}
|
| 283 |
}
|
| 284 |
+
|
| 285 |
+
messageInput.value = finalTranscript || interimTranscript;
|
| 286 |
};
|
|
|
|
| 287 |
recognition.onerror = (event) => {
|
| 288 |
+
isListening = false;
|
| 289 |
+
micButton.classList.remove('listening');
|
| 290 |
+
micButton.style.backgroundColor = '';
|
| 291 |
+
|
| 292 |
if (event.error === 'not-allowed') {
|
| 293 |
+
micStatus.textContent = 'Permission requise';
|
| 294 |
+
this._addMessage('assistant', 'Veuillez autoriser l\'accès au microphone dans les paramètres de votre navigateur.');
|
| 295 |
} else {
|
|
|
|
| 296 |
micStatus.textContent = 'Erreur micro';
|
| 297 |
+
this._addMessage('assistant', 'Désolé, je n\'ai pas pu accéder au microphone. Essayez de parler plus fort ou de vérifier vos paramètres.');
|
| 298 |
}
|
| 299 |
+
|
| 300 |
+
setTimeout(() => micStatus.textContent = 'En ligne', 3000);
|
| 301 |
};
|
| 302 |
|
| 303 |
recognition.onend = () => {
|
| 304 |
+
if (isListening) {
|
| 305 |
+
isListening = false;
|
| 306 |
micButton.classList.remove('listening');
|
| 307 |
+
micButton.style.backgroundColor = '';
|
| 308 |
micStatus.textContent = 'En ligne';
|
| 309 |
}
|
| 310 |
};
|
| 311 |
+
try {
|
|
|
|
| 312 |
recognition.start();
|
| 313 |
} catch (err) {
|
| 314 |
this._speechState = 'idle';
|
|
|
|
| 316 |
micButton.classList.remove('listening');
|
| 317 |
}
|
| 318 |
});
|
| 319 |
+
// Enhanced send message with file handling
|
|
|
|
| 320 |
const sendMessage = () => {
|
| 321 |
const message = messageInput.value.trim();
|
| 322 |
if (message || this._files.length) {
|
| 323 |
+
// User message with files
|
| 324 |
+
let userMessage = message;
|
| 325 |
+
if (this._files.length) {
|
| 326 |
+
userMessage += `\n\n📎 Fichiers: ${this._files.map(f => f.name).join(', ')}`;
|
| 327 |
+
}
|
| 328 |
|
| 329 |
+
this._addMessage('user', userMessage);
|
| 330 |
messageInput.value = '';
|
| 331 |
this._files = [];
|
| 332 |
+
filesInfo.innerHTML = '';
|
| 333 |
fileInput.value = '';
|
| 334 |
|
| 335 |
+
// Rosalinda's response
|
| 336 |
setTimeout(() => {
|
| 337 |
+
let response;
|
| 338 |
+
if (this._files.length > 0) {
|
| 339 |
+
response = `J'ai bien reçu ${this._files.length} fichier(s) avec votre message. Je les analyse maintenant...`;
|
| 340 |
+
} else if (message.toLowerCase().includes('projet')) {
|
| 341 |
+
response = "Pour votre projet, je suggère:\n1. Structure claire\n2. Design cohérent\n3. Tests approfondis\nQu'en pensez-vous?";
|
| 342 |
+
} else if (message.toLowerCase().includes('image')) {
|
| 343 |
+
response = "Je peux générer des images personnalisées. Dites-moi ce que vous imaginez.";
|
| 344 |
+
} else {
|
| 345 |
+
const responses = [
|
| 346 |
+
"Je travaille sur votre demande...",
|
| 347 |
+
"Analyse terminée. Voici mes suggestions:",
|
| 348 |
+
"J'ai une solution créative pour vous:",
|
| 349 |
+
"Voici ce que je propose:"
|
| 350 |
+
];
|
| 351 |
+
response = responses[Math.floor(Math.random() * responses.length)];
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
this._addMessage('assistant', response);
|
| 355 |
+
}, 1000);
|
| 356 |
}
|
| 357 |
};
|
| 358 |
+
sendButton.addEventListener('click', sendMessage);
|
| 359 |
messageInput.addEventListener('keydown', (e) => {
|
| 360 |
if (e.key === 'Enter') sendMessage();
|
| 361 |
});
|
style.css
CHANGED
|
@@ -48,17 +48,24 @@ custom-chat {
|
|
| 48 |
background-color: #3b82f6 !important;
|
| 49 |
color: white !important;
|
| 50 |
animation: pulse 1.5s infinite;
|
|
|
|
|
|
|
| 51 |
}
|
| 52 |
@keyframes pulse {
|
| 53 |
0% { opacity: 1; }
|
| 54 |
50% { opacity: 0.7; }
|
| 55 |
100% { opacity: 1; }
|
| 56 |
}
|
| 57 |
-
|
| 58 |
.file-info {
|
| 59 |
font-size: 0.75rem;
|
| 60 |
-
color: #
|
| 61 |
margin-top: 0.25rem;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
custom-sidebar {
|
| 64 |
width: 280px;
|
|
|
|
| 48 |
background-color: #3b82f6 !important;
|
| 49 |
color: white !important;
|
| 50 |
animation: pulse 1.5s infinite;
|
| 51 |
+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
|
| 52 |
+
transition: all 0.3s ease;
|
| 53 |
}
|
| 54 |
@keyframes pulse {
|
| 55 |
0% { opacity: 1; }
|
| 56 |
50% { opacity: 0.7; }
|
| 57 |
100% { opacity: 1; }
|
| 58 |
}
|
|
|
|
| 59 |
.file-info {
|
| 60 |
font-size: 0.75rem;
|
| 61 |
+
color: #3b82f6;
|
| 62 |
margin-top: 0.25rem;
|
| 63 |
+
display: flex;
|
| 64 |
+
align-items: center;
|
| 65 |
+
gap: 0.25rem;
|
| 66 |
+
padding: 0.25rem 0.5rem;
|
| 67 |
+
background: rgba(59, 130, 246, 0.1);
|
| 68 |
+
border-radius: 0.25rem;
|
| 69 |
}
|
| 70 |
custom-sidebar {
|
| 71 |
width: 280px;
|